diff --git a/.github/workflows/meilisearch-beta-tests.yml b/.github/workflows/meilisearch-beta-tests.yml index 5e23fcfa..a76ae1a7 100644 --- a/.github/workflows/meilisearch-beta-tests.yml +++ b/.github/workflows/meilisearch-beta-tests.yml @@ -38,13 +38,9 @@ jobs: MEILI_NO_ANALYTICS: true strategy: matrix: - php-version: ['7.4', '8.0', '8.1', '8.2', '8.3', '8.4'] + php-version: ['8.1', '8.2', '8.3', '8.4'] http-client: ['Guzzle-7', 'Guzzle-7-Adapter', 'Symfony-HttpClient', 'PHP-HTTP-CurlClient', 'Kriswallsmith-Buzz'] exclude: - - php-version: '7.4' - http-client: 'Symfony-HttpClient' - - php-version: '8.0' - http-client: 'Symfony-HttpClient' - php-version: '8.1' http-client: 'Symfony-HttpClient' diff --git a/.github/workflows/pre-release-tests.yml b/.github/workflows/pre-release-tests.yml index e12a7136..faaa93cb 100644 --- a/.github/workflows/pre-release-tests.yml +++ b/.github/workflows/pre-release-tests.yml @@ -38,13 +38,9 @@ jobs: MEILI_NO_ANALYTICS: true strategy: matrix: - php-version: ['7.4', '8.0', '8.1', '8.2', '8.3', '8.4'] + php-version: ['8.1', '8.2', '8.3', '8.4'] http-client: ['Guzzle-7', 'Guzzle-7-Adapter', 'Symfony-HttpClient', 'PHP-HTTP-CurlClient', 'Kriswallsmith-Buzz'] exclude: - - php-version: '7.4' - http-client: 'Symfony-HttpClient' - - php-version: '8.0' - http-client: 'Symfony-HttpClient' - php-version: '8.1' http-client: 'Symfony-HttpClient' steps: @@ -88,39 +84,3 @@ jobs: - name: Run test suite run: sh scripts/tests.sh - - test_php_7_guzzle_6: - runs-on: ubuntu-latest - needs: ['meilisearch-version'] - name: integration-tests-against-rc (PHP 7.4 & Guzzle 6) - services: - meilisearch: - image: getmeili/meilisearch:${{ needs.meilisearch-version.outputs.version }} - env: - MEILI_MASTER_KEY: 'masterKey' - MEILI_NO_ANALYTICS: 'true' - ports: - - '7700:7700' - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Install PHP - uses: shivammathur/setup-php@v2 - with: - php-version: '7.4' - coverage: none - - - name: Validate composer.json and composer.lock - run: composer validate - - - name: Switch to Guzzle 6 - run: | - sed -i 's/"guzzlehttp\/guzzle": "^[0-9]\+\.[0-9]\+\.[0-9]\+\",/"php-http\/guzzle6-adapter": "^2.0.2",/' composer.json - - - name: Install dependencies - uses: ramsey/composer-install@v3 - - - name: Run test suite - php-http/guzzle6-adapter - run: sh scripts/tests.sh diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 2bc67c2f..b53b5c4d 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -79,13 +79,9 @@ jobs: MEILI_NO_ANALYTICS: true strategy: matrix: - php-version: ['7.4', '8.0', '8.1', '8.2', '8.3', '8.4'] + php-version: ['8.1', '8.2', '8.3', '8.4'] http-client: ['Guzzle-7', 'Guzzle-7-Adapter', 'Symfony-HttpClient', 'PHP-HTTP-CurlClient', 'Kriswallsmith-Buzz'] exclude: - - php-version: '7.4' - http-client: 'Symfony-HttpClient' - - php-version: '8.0' - http-client: 'Symfony-HttpClient' - php-version: '8.1' http-client: 'Symfony-HttpClient' @@ -136,44 +132,6 @@ jobs: name: 'phpunit-${{ matrix.php-version }}-${{ matrix.http-client }}-coverage' path: 'coverage*.xml' - test_php_7_guzzle_6: - # Will not run if the event is a PR to bump-meilisearch-v* (so a pre-release PR) - # Will still run for each push to bump-meilisearch-v* - if: github.event_name != 'pull_request' || !startsWith(github.base_ref, 'bump-meilisearch-v') - runs-on: ubuntu-latest - services: - meilisearch: - image: getmeili/meilisearch:latest - ports: - - 7700:7700 - env: - MEILI_MASTER_KEY: masterKey - MEILI_NO_ANALYTICS: true - - name: integration-tests (PHP 7.4 & Guzzle 6) - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Install PHP - uses: shivammathur/setup-php@v2 - with: - php-version: '7.4' - coverage: none - - - name: Validate composer.json and composer.lock - run: composer validate - - - name: Switch to Guzzle 6 - run: | - sed -i 's/"guzzlehttp\/guzzle": "^[0-9]\+\.[0-9]\+\.[0-9]\+\",/"php-http\/guzzle6-adapter": "^2.0.2",/' composer.json - - - name: Install dependencies - uses: ramsey/composer-install@v3 - - - name: Run test suite - php-http/guzzle6-adapter - run: sh scripts/tests.sh - upload-coverage: name: Upload coverage to Codecov runs-on: ubuntu-latest diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php index 897ce305..2bcdc65b 100644 --- a/.php-cs-fixer.dist.php +++ b/.php-cs-fixer.dist.php @@ -15,9 +15,8 @@ 'void_return' => true, 'native_function_invocation' => ['include' => ['@compiler_optimized'], 'scope' => 'namespaced'], 'php_unit_test_case_static_method_calls' => ['call_type' => 'self'], - 'php_unit_strict' => true, - // @todo: when we'll support only PHP 8.0 and upper, we can enable `parameters` for `trailing_comma_in_multiline` rule - 'trailing_comma_in_multiline' => ['after_heredoc' => true, 'elements' => ['array_destructuring', 'arrays', 'match'/* , 'parameters' */]], + 'php_unit_strict' => false, + 'trailing_comma_in_multiline' => ['after_heredoc' => true, 'elements' => ['array_destructuring', 'arrays', 'match', 'parameters']], ]) ->setRiskyAllowed(true) ->setFinder($finder); diff --git a/composer.json b/composer.json index 755350fa..3665c820 100644 --- a/composer.json +++ b/composer.json @@ -19,16 +19,19 @@ ], "minimum-stability": "stable", "require": { - "php": "^7.4 || ^8.0", + "php": "^8.1", "ext-json": "*", - "php-http/discovery": "^1.7", + "php-http/discovery": "^1.19", "psr/http-client": "^1.0" }, "autoload": { "psr-4": { "MeiliSearch\\": "src/", "Meilisearch\\": "src/" - } + }, + "files": [ + "src/functions.php" + ] }, "autoload-dev": { "psr-4": { @@ -40,7 +43,7 @@ "http-interop/http-factory-guzzle": "Factory for guzzlehttp/guzzle" }, "require-dev": { - "phpunit/phpunit": "^9.5 || ^10.5", + "phpunit/phpunit": "^10.5", "php-cs-fixer/shim": "^3.59.3", "guzzlehttp/guzzle": "^7.8.1", "http-interop/http-factory-guzzle": "^1.2.0", diff --git a/phpunit.xml.dist b/phpunit.xml.dist index fe2d97ac..34ed73ca 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,5 +1,18 @@ - + ./tests @@ -9,7 +22,15 @@ - + src/ diff --git a/src/Client.php b/src/Client.php index dffa598a..4a85f049 100644 --- a/src/Client.php +++ b/src/Client.php @@ -50,7 +50,7 @@ public function __construct( ?ClientInterface $httpClient = null, ?RequestFactoryInterface $requestFactory = null, array $clientAgents = [], - ?StreamFactoryInterface $streamFactory = null + ?StreamFactoryInterface $streamFactory = null, ) { $this->http = new MeilisearchClientAdapter($url, $apiKey, $httpClient, $requestFactory, $clientAgents, $streamFactory); $this->index = new Indexes($this->http); diff --git a/src/Contracts/Data.php b/src/Contracts/Data.php index 5ed52a79..4c734f69 100644 --- a/src/Contracts/Data.php +++ b/src/Contracts/Data.php @@ -6,7 +6,7 @@ class Data implements \ArrayAccess, \Countable, \IteratorAggregate { - protected $data = []; + protected array $data = []; public function __construct(array $data = []) { @@ -28,14 +28,9 @@ public function offsetUnset($offset): void unset($this->data[$offset]); } - #[\ReturnTypeWillChange] - public function offsetGet($offset) + public function offsetGet($offset): mixed { - if (isset($this->data[$offset])) { - return $this->data[$offset]; - } - - return null; + return $this->data[$offset] ?? null; } public function count(): int diff --git a/src/Contracts/Http.php b/src/Contracts/Http.php index 6f46689e..315f795a 100644 --- a/src/Contracts/Http.php +++ b/src/Contracts/Http.php @@ -20,7 +20,7 @@ public function get(string $path, array $query = []); * @throws ApiException * @throws \JsonException */ - public function post(string $path, $body = null, array $query = [], ?string $contentType = null); + public function post(string $path, mixed $body = null, array $query = [], ?string $contentType = null); /** * @param non-empty-string|null $contentType @@ -28,13 +28,13 @@ public function post(string $path, $body = null, array $query = [], ?string $con * @throws ApiException * @throws \JsonException */ - public function put(string $path, $body = null, array $query = [], ?string $contentType = null); + public function put(string $path, mixed $body = null, array $query = [], ?string $contentType = null); /** * @throws ApiException * @throws \JsonException */ - public function patch(string $path, $body = null, array $query = []); + public function patch(string $path, mixed $body = null, array $query = []); /** * @throws ApiException diff --git a/src/Contracts/SimilarDocumentsQuery.php b/src/Contracts/SimilarDocumentsQuery.php index 04c65ae3..a9285c5a 100644 --- a/src/Contracts/SimilarDocumentsQuery.php +++ b/src/Contracts/SimilarDocumentsQuery.php @@ -7,9 +7,9 @@ class SimilarDocumentsQuery { /** - * @var int|string + * @var int|non-empty-string */ - private $id; + private int|string $id; /** * @var non-empty-string @@ -42,16 +42,13 @@ class SimilarDocumentsQuery */ private ?array $filter = null; - /** - * @var int|float|null - */ - private $rankingScoreThreshold; + private int|float|null $rankingScoreThreshold = null; /** - * @param int|string $id - * @param non-empty-string $embedder + * @param int|non-empty-string $id + * @param non-empty-string $embedder */ - public function __construct($id, string $embedder) + public function __construct(int|string $id, string $embedder) { $this->id = $id; $this->embedder = $embedder; @@ -142,11 +139,9 @@ public function setRetrieveVectors(?bool $retrieveVectors): self } /** - * @param int|float|null $rankingScoreThreshold - * * @return $this */ - public function setRankingScoreThreshold($rankingScoreThreshold): self + public function setRankingScoreThreshold(int|float|null $rankingScoreThreshold): self { $this->rankingScoreThreshold = $rankingScoreThreshold; @@ -155,7 +150,7 @@ public function setRankingScoreThreshold($rankingScoreThreshold): self /** * @return array{ - * id: int|string, + * id: int|non-empty-string, * embedder: non-empty-string, * offset?: non-negative-int, * limit?: positive-int, diff --git a/src/Contracts/Task.php b/src/Contracts/Task.php new file mode 100644 index 00000000..f69babdd --- /dev/null +++ b/src/Contracts/Task.php @@ -0,0 +1,185 @@ +taskUid; + } + + /** + * @return non-empty-string|null + */ + public function getIndexUid(): ?string + { + return $this->indexUid; + } + + public function getStatus(): TaskStatus + { + return $this->status; + } + + public function getType(): TaskType + { + return $this->type; + } + + public function getEnqueuedAt(): \DateTimeImmutable + { + return $this->enqueuedAt; + } + + public function getStartedAt(): ?\DateTimeImmutable + { + return $this->startedAt; + } + + public function getFinishedAt(): ?\DateTimeImmutable + { + return $this->finishedAt; + } + + /** + * @return non-empty-string|null + */ + public function getDuration(): ?string + { + return $this->duration; + } + + public function getCanceledBy(): ?int + { + return $this->canceledBy; + } + + public function getBatchUid(): ?int + { + return $this->batchUid; + } + + public function getDetails(): ?TaskDetails + { + return $this->details; + } + + public function getError(): ?TaskError + { + return $this->error; + } + + public function isFinished(): bool + { + return TaskStatus::Enqueued !== $this->status && TaskStatus::Processing !== $this->status; + } + + public function wait(int $timeoutInMs = 5000, int $intervalInMs = 50): Task + { + if ($this->isFinished()) { + return $this; + } + + if (null !== $this->await) { + return ($this->await)($this->taskUid, $timeoutInMs, $intervalInMs); + } + + throw new LogicException(\sprintf('Cannot wait for task because wait function is not provided.')); + } + + /** + * @param array{ + * taskUid?: int, + * uid?: int, + * indexUid?: non-empty-string, + * status: non-empty-string, + * type: non-empty-string, + * enqueuedAt: non-empty-string, + * startedAt?: non-empty-string|null, + * finishedAt?: non-empty-string|null, + * duration?: non-empty-string|null, + * canceledBy?: int, + * batchUid?: int, + * details?: array|null, + * error?: array|null + * } $data + * @param \Closure(int, int, int): Task|null $await + */ + public static function fromArray(array $data, ?\Closure $await = null): Task + { + $details = $data['details'] ?? null; + + return new self( + $data['taskUid'] ?? $data['uid'], + $data['indexUid'] ?? null, + TaskStatus::from($data['status']), + $type = TaskType::from($data['type']), + new \DateTimeImmutable($data['enqueuedAt']), + \array_key_exists('startedAt', $data) && null !== $data['startedAt'] ? new \DateTimeImmutable($data['startedAt']) : null, + \array_key_exists('finishedAt', $data) && null !== $data['finishedAt'] ? new \DateTimeImmutable($data['finishedAt']) : null, + $data['duration'] ?? null, + $data['canceledBy'] ?? null, + $data['batchUid'] ?? null, + match ($type) { + TaskType::IndexCreation => null !== $details ? IndexCreationDetails::fromArray($details) : null, + TaskType::IndexUpdate => null !== $details ? IndexUpdateDetails::fromArray($details) : null, + TaskType::IndexDeletion => null !== $details ? IndexDeletionDetails::fromArray($details) : null, + TaskType::IndexSwap => null !== $details ? IndexSwapDetails::fromArray($details) : null, + TaskType::DocumentAdditionOrUpdate => null !== $details ? DocumentAdditionOrUpdateDetails::fromArray($details) : null, + TaskType::DocumentDeletion => null !== $details ? DocumentDeletionDetails::fromArray($details) : null, + TaskType::DocumentEdition => null !== $details ? DocumentEditionDetails::fromArray($details) : null, + TaskType::SettingsUpdate => null !== $details ? SettingsUpdateDetails::fromArray($details) : null, + TaskType::DumpCreation => null !== $details ? DumpCreationDetails::fromArray($details) : null, + TaskType::TaskCancelation => null !== $details ? TaskCancelationDetails::fromArray($details) : null, + TaskType::TaskDeletion => null !== $details ? TaskDeletionDetails::fromArray($details) : null, + // It’s intentional that SnapshotCreation tasks don’t have a details object + // (no SnapshotCreationDetails exists and tests don’t exercise any details) + TaskType::SnapshotCreation => null, + }, + \array_key_exists('error', $data) && null !== $data['error'] ? TaskError::fromArray($data['error']) : null, + $await, + ); + } +} diff --git a/src/Contracts/TaskDetails.php b/src/Contracts/TaskDetails.php new file mode 100644 index 00000000..44cffa08 --- /dev/null +++ b/src/Contracts/TaskDetails.php @@ -0,0 +1,16 @@ + + */ +final class DocumentAdditionOrUpdateDetails implements TaskDetails +{ + /** + * @param non-negative-int $receivedDocuments number of documents received + * @param non-negative-int|null $indexedDocuments Number of documents indexed. `null` while the task status is enqueued or processing. + */ + public function __construct( + public readonly int $receivedDocuments, + public readonly ?int $indexedDocuments, + ) { + } + + public static function fromArray(array $data): self + { + return new self( + $data['receivedDocuments'], + $data['indexedDocuments'] ?? null, + ); + } +} diff --git a/src/Contracts/TaskDetails/DocumentDeletionDetails.php b/src/Contracts/TaskDetails/DocumentDeletionDetails.php new file mode 100644 index 00000000..74ac8d55 --- /dev/null +++ b/src/Contracts/TaskDetails/DocumentDeletionDetails.php @@ -0,0 +1,38 @@ + + */ +final class DocumentDeletionDetails implements TaskDetails +{ + /** + * @param non-negative-int|null $providedIds number of documents queued for deletion + * @param string|null $originalFilter The filter used to delete documents. Null if it was not specified. + * @param int|null $deletedDocuments Number of documents deleted. `null` while the task status is enqueued or processing. + */ + public function __construct( + public readonly ?int $providedIds, + public readonly ?string $originalFilter, + public readonly ?int $deletedDocuments, + ) { + } + + public static function fromArray(array $data): self + { + return new self( + $data['providedIds'] ?? null, + $data['originalFilter'] ?? null, + $data['deletedDocuments'] ?? null, + ); + } +} diff --git a/src/Contracts/TaskDetails/DocumentEditionDetails.php b/src/Contracts/TaskDetails/DocumentEditionDetails.php new file mode 100644 index 00000000..3186d43b --- /dev/null +++ b/src/Contracts/TaskDetails/DocumentEditionDetails.php @@ -0,0 +1,42 @@ +, + * deletedDocuments: non-negative-int|null, + * editedDocuments: non-negative-int|null, + * function: string|null, + * originalFilter: string|null + * }> + */ +final class DocumentEditionDetails implements TaskDetails +{ + /** + * @param array $context + */ + public function __construct( + public readonly array $context, + public readonly ?int $deletedDocuments, + public readonly ?int $editedDocuments, + public readonly ?string $function, + public readonly ?string $originalFilter, + ) { + } + + public static function fromArray(array $data): self + { + return new self( + $data['context'], + $data['deletedDocuments'], + $data['editedDocuments'], + $data['function'], + $data['originalFilter'], + ); + } +} diff --git a/src/Contracts/TaskDetails/DumpCreationDetails.php b/src/Contracts/TaskDetails/DumpCreationDetails.php new file mode 100644 index 00000000..14f34edb --- /dev/null +++ b/src/Contracts/TaskDetails/DumpCreationDetails.php @@ -0,0 +1,30 @@ + + */ +final class DumpCreationDetails implements TaskDetails +{ + /** + * @param non-empty-string|null $dumpUid + */ + public function __construct( + public readonly ?string $dumpUid, + ) { + } + + public static function fromArray(array $data): self + { + return new self( + $data['dumpUid'], + ); + } +} diff --git a/src/Contracts/TaskDetails/IndexCreationDetails.php b/src/Contracts/TaskDetails/IndexCreationDetails.php new file mode 100644 index 00000000..98e576ad --- /dev/null +++ b/src/Contracts/TaskDetails/IndexCreationDetails.php @@ -0,0 +1,30 @@ + + */ +final class IndexCreationDetails implements TaskDetails +{ + /** + * @param non-empty-string|null $primaryKey Value of the primaryKey field supplied during index creation. `null` if it was not specified. + */ + public function __construct( + public readonly ?string $primaryKey, + ) { + } + + public static function fromArray(array $data): self + { + return new self( + $data['primaryKey'], + ); + } +} diff --git a/src/Contracts/TaskDetails/IndexDeletionDetails.php b/src/Contracts/TaskDetails/IndexDeletionDetails.php new file mode 100644 index 00000000..095cd7d5 --- /dev/null +++ b/src/Contracts/TaskDetails/IndexDeletionDetails.php @@ -0,0 +1,30 @@ + + */ +final class IndexDeletionDetails implements TaskDetails +{ + /** + * @param non-negative-int|null $deletedDocuments Number of deleted documents. This should equal the total number of documents in the deleted index. `null` while the task status is enqueued or processing. + */ + public function __construct( + public readonly ?int $deletedDocuments, + ) { + } + + public static function fromArray(array $data): self + { + return new self( + $data['deletedDocuments'], + ); + } +} diff --git a/src/Contracts/TaskDetails/IndexSwapDetails.php b/src/Contracts/TaskDetails/IndexSwapDetails.php new file mode 100644 index 00000000..56493685 --- /dev/null +++ b/src/Contracts/TaskDetails/IndexSwapDetails.php @@ -0,0 +1,30 @@ + + * }> + */ +final class IndexSwapDetails implements TaskDetails +{ + /** + * @param array $swaps + */ + public function __construct( + public readonly array $swaps, + ) { + } + + public static function fromArray(array $data): self + { + return new self( + $data['swaps'], + ); + } +} diff --git a/src/Contracts/TaskDetails/IndexUpdateDetails.php b/src/Contracts/TaskDetails/IndexUpdateDetails.php new file mode 100644 index 00000000..7603a081 --- /dev/null +++ b/src/Contracts/TaskDetails/IndexUpdateDetails.php @@ -0,0 +1,30 @@ + + */ +final class IndexUpdateDetails implements TaskDetails +{ + /** + * @param non-empty-string|null $primaryKey Value of the primaryKey field supplied during index creation. `null` if it was not specified. + */ + public function __construct( + public readonly ?string $primaryKey, + ) { + } + + public static function fromArray(array $data): self + { + return new self( + $data['primaryKey'], + ); + } +} diff --git a/src/Contracts/TaskDetails/SettingsUpdateDetails.php b/src/Contracts/TaskDetails/SettingsUpdateDetails.php new file mode 100644 index 00000000..90c5e18c --- /dev/null +++ b/src/Contracts/TaskDetails/SettingsUpdateDetails.php @@ -0,0 +1,148 @@ +, + * displayedAttributes?: list, + * distinctAttribute?: string, + * embedders?: non-empty-array, + * response?: array, + * revision?: string, + * searchEmbedder?: array{model: string, source: string}, + * source?: string, + * url?: string + * }>, + * faceting?: array{maxValuesPerFacet: non-negative-int, sortFacetValuesBy: array}|null, + * facetSearch?: bool, + * filterableAttributes?: list, features: array{facetSearch: bool, filter: array{equality: bool, comparison: bool}}}>|null, + * localizedAttributes?: list, attributePatterns: list}>, + * nonSeparatorTokens?: list, + * pagination?: array{maxTotalHits: non-negative-int}, + * prefixSearch?: non-empty-string|null, + * proximityPrecision?: 'byWord'|'byAttribute', + * rankingRules?: list, + * searchableAttributes?: list, + * searchCutoffMs?: non-negative-int, + * separatorTokens?: list, + * sortableAttributes?: list, + * stopWords?: list, + * synonyms?: array>, + * typoTolerance?: array{ + * enabled: bool, + * minWordSizeForTypos: array{oneTypo: int, twoTypos: int}, + * disableOnWords: list, + * disableOnAttributes: list, + * disableOnNumbers: bool + * } + * }> + */ +final class SettingsUpdateDetails implements TaskDetails +{ + /** + * @param list|null $dictionary + * @param list|null $displayedAttributes + * @param non-empty-array, + * response?: array, + * revision?: string, + * searchEmbedder?: array{model: string, source: string}, + * source?: string, + * url?: string + * }>|null $embedders + * @param array{maxValuesPerFacet: non-negative-int, sortFacetValuesBy: array}|null $faceting + * @param list, features: array{facetSearch: bool, filter: array{equality: bool, comparison: bool}}}>|null $filterableAttributes + * @param list, attributePatterns: list}>|null $localizedAttributes + * @param list|null $nonSeparatorTokens + * @param array{maxTotalHits: non-negative-int}|null $pagination + * @param 'indexingTime'|'disabled'|null $prefixSearch + * @param 'byWord'|'byAttribute'|null $proximityPrecision + * @param list|null $rankingRules + * @param list|null $searchableAttributes + * @param non-negative-int|null $searchCutoffMs + * @param list $separatorTokens + * @param list|null $sortableAttributes + * @param list|null $stopWords + * @param array>|null $synonyms + * @param array{ + * enabled: bool, + * minWordSizeForTypos: array{oneTypo: int, twoTypos: int}, + * disableOnWords: list, + * disableOnAttributes: list, + * disableOnNumbers: bool + * }|null $typoTolerance + */ + public function __construct( + public readonly ?array $dictionary, + public readonly ?array $displayedAttributes, + public readonly ?string $distinctAttribute, + public readonly ?array $embedders, + public readonly ?array $faceting, + public readonly ?bool $facetSearch, + public readonly ?array $filterableAttributes, + public readonly ?array $localizedAttributes, + public readonly ?array $nonSeparatorTokens, + public readonly ?array $pagination, + public readonly ?string $prefixSearch, + public readonly ?string $proximityPrecision, + public readonly ?array $rankingRules, + public readonly ?array $searchableAttributes, + public readonly ?int $searchCutoffMs, + public readonly ?array $separatorTokens, + public readonly ?array $sortableAttributes, + public readonly ?array $stopWords, + public readonly ?array $synonyms, + public readonly ?array $typoTolerance, + ) { + } + + public static function fromArray(array $data): self + { + return new self( + $data['dictionary'] ?? null, + $data['displayedAttributes'] ?? null, + $data['distinctAttribute'] ?? null, + $data['embedders'] ?? null, + $data['faceting'] ?? null, + $data['facetSearch'] ?? null, + $data['filterableAttributes'] ?? null, + $data['localizedAttributes'] ?? null, + $data['nonSeparatorTokens'] ?? null, + $data['pagination'] ?? null, + $data['prefixSearch'] ?? null, + $data['proximityPrecision'] ?? null, + $data['rankingRules'] ?? null, + $data['searchableAttributes'] ?? null, + $data['searchCutoffMs'] ?? null, + $data['separatorTokens'] ?? null, + $data['sortableAttributes'] ?? null, + $data['stopWords'] ?? null, + $data['synonyms'] ?? null, + $data['typoTolerance'] ?? null, + ); + } +} diff --git a/src/Contracts/TaskDetails/TaskCancelationDetails.php b/src/Contracts/TaskDetails/TaskCancelationDetails.php new file mode 100644 index 00000000..2d394cb7 --- /dev/null +++ b/src/Contracts/TaskDetails/TaskCancelationDetails.php @@ -0,0 +1,38 @@ + + */ +final class TaskCancelationDetails implements TaskDetails +{ + /** + * @param non-negative-int|null $matchedTasks The number of matched tasks. If the API key used for the request doesn’t have access to an index, tasks relating to that index will not be included in matchedTasks. + * @param non-negative-int|null $canceledTasks The number of tasks successfully canceled. If the task cancellation fails, this will be 0. null when the task status is enqueued or processing. + * @param string|null $originalFilter the filter used in the cancel task request + */ + public function __construct( + public readonly ?int $matchedTasks, + public readonly ?int $canceledTasks, + public readonly ?string $originalFilter, + ) { + } + + public static function fromArray(array $data): self + { + return new self( + $data['matchedTasks'], + $data['canceledTasks'], + $data['originalFilter'], + ); + } +} diff --git a/src/Contracts/TaskDetails/TaskDeletionDetails.php b/src/Contracts/TaskDetails/TaskDeletionDetails.php new file mode 100644 index 00000000..70397e36 --- /dev/null +++ b/src/Contracts/TaskDetails/TaskDeletionDetails.php @@ -0,0 +1,38 @@ + + */ +final class TaskDeletionDetails implements TaskDetails +{ + /** + * @param non-negative-int|null $matchedTasks The number of matched tasks. If the API key used for the request doesn’t have access to an index, tasks relating to that index will not be included in matchedTasks. + * @param non-negative-int|null $deletedTasks The number of tasks successfully deleted. If the task deletion fails, this will be 0. null when the task status is enqueued or processing. + * @param string|null $originalFilter the filter used in the delete task request + */ + public function __construct( + public readonly ?int $matchedTasks, + public readonly ?int $deletedTasks, + public readonly ?string $originalFilter, + ) { + } + + public static function fromArray(array $data): self + { + return new self( + $data['matchedTasks'], + $data['deletedTasks'], + $data['originalFilter'], + ); + } +} diff --git a/src/Contracts/TaskError.php b/src/Contracts/TaskError.php new file mode 100644 index 00000000..66696bdd --- /dev/null +++ b/src/Contracts/TaskError.php @@ -0,0 +1,40 @@ + Task::fromArray($data), $params['results']) : []); $this->from = $params['from'] ?? 0; $this->limit = $params['limit'] ?? 0; @@ -37,7 +37,7 @@ public function __construct(array $params) } /** - * @return array + * @return array */ public function getResults(): array { diff --git a/src/Endpoints/Batches.php b/src/Endpoints/Batches.php index 1a00b475..6562b93c 100644 --- a/src/Endpoints/Batches.php +++ b/src/Endpoints/Batches.php @@ -10,7 +10,7 @@ class Batches extends Endpoint { protected const PATH = '/batches'; - public function get($batchUid): array + public function get(int $batchUid): array { return $this->http->get(self::PATH.'/'.$batchUid); } diff --git a/src/Endpoints/Delegates/HandlesBatches.php b/src/Endpoints/Delegates/HandlesBatches.php index 5c53bc9d..542bd221 100644 --- a/src/Endpoints/Delegates/HandlesBatches.php +++ b/src/Endpoints/Delegates/HandlesBatches.php @@ -12,7 +12,7 @@ trait HandlesBatches { protected Batches $batches; - public function getBatch($uid): array + public function getBatch(int $uid): array { return $this->batches->get($uid); } diff --git a/src/Endpoints/Delegates/HandlesDocuments.php b/src/Endpoints/Delegates/HandlesDocuments.php index f461d2bf..f21bd1d2 100644 --- a/src/Endpoints/Delegates/HandlesDocuments.php +++ b/src/Endpoints/Delegates/HandlesDocuments.php @@ -6,15 +6,20 @@ use Meilisearch\Contracts\DocumentsQuery; use Meilisearch\Contracts\DocumentsResults; +use Meilisearch\Contracts\Task; +use Meilisearch\Endpoints\Tasks; use Meilisearch\Exceptions\ApiException; -use Meilisearch\Exceptions\InvalidArgumentException; use Meilisearch\Exceptions\InvalidResponseBodyException; +use function Meilisearch\partial; + trait HandlesDocuments { - public function getDocument($documentId, ?array $fields = null) + /** + * @param non-empty-string|int $documentId + */ + public function getDocument(string|int $documentId, ?array $fields = null): array { - $this->assertValidDocumentId($documentId); $query = isset($fields) ? ['fields' => implode(',', $fields)] : []; return $this->http->get(self::PATH.'/'.$this->uid.'/documents/'.$documentId, $query); @@ -38,27 +43,30 @@ public function getDocuments(?DocumentsQuery $options = null): DocumentsResults } } - public function addDocuments(array $documents, ?string $primaryKey = null) + public function addDocuments(array $documents, ?string $primaryKey = null): Task { - return $this->http->post(self::PATH.'/'.$this->uid.'/documents', $documents, ['primaryKey' => $primaryKey]); + return Task::fromArray($this->http->post(self::PATH.'/'.$this->uid.'/documents', $documents, ['primaryKey' => $primaryKey]), partial(Tasks::waitTask(...), $this->http)); } - public function addDocumentsJson(string $documents, ?string $primaryKey = null) + public function addDocumentsJson(string $documents, ?string $primaryKey = null): Task { - return $this->http->post(self::PATH.'/'.$this->uid.'/documents', $documents, ['primaryKey' => $primaryKey], 'application/json'); + return Task::fromArray($this->http->post(self::PATH.'/'.$this->uid.'/documents', $documents, ['primaryKey' => $primaryKey], 'application/json'), partial(Tasks::waitTask(...), $this->http)); } - public function addDocumentsCsv(string $documents, ?string $primaryKey = null, ?string $delimiter = null) + public function addDocumentsCsv(string $documents, ?string $primaryKey = null, ?string $delimiter = null): Task { - return $this->http->post(self::PATH.'/'.$this->uid.'/documents', $documents, ['primaryKey' => $primaryKey, 'csvDelimiter' => $delimiter], 'text/csv'); + return Task::fromArray($this->http->post(self::PATH.'/'.$this->uid.'/documents', $documents, ['primaryKey' => $primaryKey, 'csvDelimiter' => $delimiter], 'text/csv'), partial(Tasks::waitTask(...), $this->http)); } - public function addDocumentsNdjson(string $documents, ?string $primaryKey = null) + public function addDocumentsNdjson(string $documents, ?string $primaryKey = null): Task { - return $this->http->post(self::PATH.'/'.$this->uid.'/documents', $documents, ['primaryKey' => $primaryKey], 'application/x-ndjson'); + return Task::fromArray($this->http->post(self::PATH.'/'.$this->uid.'/documents', $documents, ['primaryKey' => $primaryKey], 'application/x-ndjson'), partial(Tasks::waitTask(...), $this->http)); } - public function addDocumentsInBatches(array $documents, ?int $batchSize = 1000, ?string $primaryKey = null) + /** + * @return list + */ + public function addDocumentsInBatches(array $documents, ?int $batchSize = 1000, ?string $primaryKey = null): array { $promises = []; @@ -69,7 +77,10 @@ public function addDocumentsInBatches(array $documents, ?int $batchSize = 1000, return $promises; } - public function addDocumentsCsvInBatches(string $documents, ?int $batchSize = 1000, ?string $primaryKey = null, ?string $delimiter = null) + /** + * @return list + */ + public function addDocumentsCsvInBatches(string $documents, ?int $batchSize = 1000, ?string $primaryKey = null, ?string $delimiter = null): array { $promises = []; @@ -80,7 +91,10 @@ public function addDocumentsCsvInBatches(string $documents, ?int $batchSize = 10 return $promises; } - public function addDocumentsNdjsonInBatches(string $documents, ?int $batchSize = 1000, ?string $primaryKey = null) + /** + * @return list + */ + public function addDocumentsNdjsonInBatches(string $documents, ?int $batchSize = 1000, ?string $primaryKey = null): array { $promises = []; @@ -91,27 +105,30 @@ public function addDocumentsNdjsonInBatches(string $documents, ?int $batchSize = return $promises; } - public function updateDocuments(array $documents, ?string $primaryKey = null) + public function updateDocuments(array $documents, ?string $primaryKey = null): Task { - return $this->http->put(self::PATH.'/'.$this->uid.'/documents', $documents, ['primaryKey' => $primaryKey]); + return Task::fromArray($this->http->put(self::PATH.'/'.$this->uid.'/documents', $documents, ['primaryKey' => $primaryKey]), partial(Tasks::waitTask(...), $this->http)); } - public function updateDocumentsJson(string $documents, ?string $primaryKey = null) + public function updateDocumentsJson(string $documents, ?string $primaryKey = null): Task { - return $this->http->put(self::PATH.'/'.$this->uid.'/documents', $documents, ['primaryKey' => $primaryKey], 'application/json'); + return Task::fromArray($this->http->put(self::PATH.'/'.$this->uid.'/documents', $documents, ['primaryKey' => $primaryKey], 'application/json'), partial(Tasks::waitTask(...), $this->http)); } - public function updateDocumentsCsv(string $documents, ?string $primaryKey = null, ?string $delimiter = null) + public function updateDocumentsCsv(string $documents, ?string $primaryKey = null, ?string $delimiter = null): Task { - return $this->http->put(self::PATH.'/'.$this->uid.'/documents', $documents, ['primaryKey' => $primaryKey, 'csvDelimiter' => $delimiter], 'text/csv'); + return Task::fromArray($this->http->put(self::PATH.'/'.$this->uid.'/documents', $documents, ['primaryKey' => $primaryKey, 'csvDelimiter' => $delimiter], 'text/csv'), partial(Tasks::waitTask(...), $this->http)); } - public function updateDocumentsNdjson(string $documents, ?string $primaryKey = null) + public function updateDocumentsNdjson(string $documents, ?string $primaryKey = null): Task { - return $this->http->put(self::PATH.'/'.$this->uid.'/documents', $documents, ['primaryKey' => $primaryKey], 'application/x-ndjson'); + return Task::fromArray($this->http->put(self::PATH.'/'.$this->uid.'/documents', $documents, ['primaryKey' => $primaryKey], 'application/x-ndjson'), partial(Tasks::waitTask(...), $this->http)); } - public function updateDocumentsInBatches(array $documents, ?int $batchSize = 1000, ?string $primaryKey = null) + /** + * @return list + */ + public function updateDocumentsInBatches(array $documents, ?int $batchSize = 1000, ?string $primaryKey = null): array { $promises = []; @@ -122,7 +139,10 @@ public function updateDocumentsInBatches(array $documents, ?int $batchSize = 100 return $promises; } - public function updateDocumentsCsvInBatches(string $documents, ?int $batchSize = 1000, ?string $primaryKey = null, ?string $delimiter = null) + /** + * @return list + */ + public function updateDocumentsCsvInBatches(string $documents, ?int $batchSize = 1000, ?string $primaryKey = null, ?string $delimiter = null): array { $promises = []; @@ -133,7 +153,10 @@ public function updateDocumentsCsvInBatches(string $documents, ?int $batchSize = return $promises; } - public function updateDocumentsNdjsonInBatches(string $documents, ?int $batchSize = 1000, ?string $primaryKey = null) + /** + * @return list + */ + public function updateDocumentsNdjsonInBatches(string $documents, ?int $batchSize = 1000, ?string $primaryKey = null): array { $promises = []; @@ -154,49 +177,36 @@ public function updateDocumentsNdjsonInBatches(string $documents, ?int $batchSiz * @param non-empty-string $function * @param array{filter?: non-empty-string|list|null, context?: array} $options */ - public function updateDocumentsByFunction(string $function, array $options = []) + public function updateDocumentsByFunction(string $function, array $options = []): Task { - return $this->http->post(self::PATH.'/'.$this->uid.'/documents/edit', array_merge(['function' => $function], $options)); + return Task::fromArray($this->http->post(self::PATH.'/'.$this->uid.'/documents/edit', array_merge(['function' => $function], $options)), partial(Tasks::waitTask(...), $this->http)); } - public function deleteAllDocuments(): array + public function deleteAllDocuments(): Task { - return $this->http->delete(self::PATH.'/'.$this->uid.'/documents'); + return Task::fromArray($this->http->delete(self::PATH.'/'.$this->uid.'/documents'), partial(Tasks::waitTask(...), $this->http)); } - public function deleteDocument($documentId): array + public function deleteDocument(string|int $documentId): Task { - $this->assertValidDocumentId($documentId); - - return $this->http->delete(self::PATH.'/'.$this->uid.'/documents/'.$documentId); + return Task::fromArray($this->http->delete(self::PATH.'/'.$this->uid.'/documents/'.$documentId), partial(Tasks::waitTask(...), $this->http)); } - public function deleteDocuments(array $options): array + public function deleteDocuments(array $options): Task { try { if (\array_key_exists('filter', $options) && $options['filter']) { - return $this->http->post(self::PATH.'/'.$this->uid.'/documents/delete', $options); + return Task::fromArray($this->http->post(self::PATH.'/'.$this->uid.'/documents/delete', $options), partial(Tasks::waitTask(...), $this->http)); } // backwards compatibility: // expect to be a array to send alongside as $documents_ids. - return $this->http->post(self::PATH.'/'.$this->uid.'/documents/delete-batch', $options); + return Task::fromArray($this->http->post(self::PATH.'/'.$this->uid.'/documents/delete-batch', $options), partial(Tasks::waitTask(...), $this->http)); } catch (InvalidResponseBodyException $e) { throw ApiException::rethrowWithHint($e, __FUNCTION__); } } - private function assertValidDocumentId($documentId): void - { - if (!\is_string($documentId) && !\is_int($documentId)) { - throw InvalidArgumentException::invalidType('documentId', ['string', 'int']); - } - - if (\is_string($documentId) && '' === trim($documentId)) { - throw InvalidArgumentException::emptyArgument('documentId'); - } - } - private static function batchCsvString(string $documents, int $batchSize): \Generator { $parsedDocuments = preg_split("/\r\n|\n|\r/", trim($documents)); diff --git a/src/Endpoints/Delegates/HandlesDumps.php b/src/Endpoints/Delegates/HandlesDumps.php index 7e73f87e..e22d9a46 100644 --- a/src/Endpoints/Delegates/HandlesDumps.php +++ b/src/Endpoints/Delegates/HandlesDumps.php @@ -4,13 +4,14 @@ namespace Meilisearch\Endpoints\Delegates; +use Meilisearch\Contracts\Task; use Meilisearch\Endpoints\Dumps; trait HandlesDumps { protected Dumps $dumps; - public function createDump(): array + public function createDump(): Task { return $this->dumps->create(); } diff --git a/src/Endpoints/Delegates/HandlesIndex.php b/src/Endpoints/Delegates/HandlesIndex.php index 983107ad..af7e3b63 100644 --- a/src/Endpoints/Delegates/HandlesIndex.php +++ b/src/Endpoints/Delegates/HandlesIndex.php @@ -6,6 +6,7 @@ use Meilisearch\Contracts\IndexesQuery; use Meilisearch\Contracts\IndexesResults; +use Meilisearch\Contracts\Task; use Meilisearch\Endpoints\Indexes; trait HandlesIndex @@ -17,32 +18,50 @@ public function getIndexes(?IndexesQuery $options = null): IndexesResults return $this->index->all($options ?? null); } + /** + * @param non-empty-string $uid + */ public function getRawIndex(string $uid): array { return $this->index($uid)->fetchRawInfo(); } + /** + * @param non-empty-string $uid + */ public function index(string $uid): Indexes { return new Indexes($this->http, $uid); } + /** + * @param non-empty-string $uid + */ public function getIndex(string $uid): Indexes { return $this->index($uid)->fetchInfo(); } - public function deleteIndex(string $uid): array + /** + * @param non-empty-string $uid + */ + public function deleteIndex(string $uid): Task { return $this->index($uid)->delete(); } - public function createIndex(string $uid, array $options = []): array + /** + * @param non-empty-string $uid + */ + public function createIndex(string $uid, array $options = []): Task { return $this->index->create($uid, $options); } - public function updateIndex(string $uid, array $options = []): array + /** + * @param non-empty-string $uid + */ + public function updateIndex(string $uid, array $options = []): Task { return $this->index($uid)->update($options); } diff --git a/src/Endpoints/Delegates/HandlesSettings.php b/src/Endpoints/Delegates/HandlesSettings.php index 112b5c81..57f41474 100644 --- a/src/Endpoints/Delegates/HandlesSettings.php +++ b/src/Endpoints/Delegates/HandlesSettings.php @@ -7,6 +7,10 @@ use Meilisearch\Contracts\Index\Faceting; use Meilisearch\Contracts\Index\Synonyms; use Meilisearch\Contracts\Index\TypoTolerance; +use Meilisearch\Contracts\Task; +use Meilisearch\Endpoints\Tasks; + +use function Meilisearch\partial; trait HandlesSettings { @@ -23,14 +27,14 @@ public function getRankingRules(): array /** * @param list $rankingRules */ - public function updateRankingRules(array $rankingRules): array + public function updateRankingRules(array $rankingRules): Task { - return $this->http->put(self::PATH.'/'.$this->uid.'/settings/ranking-rules', $rankingRules); + return Task::fromArray($this->http->put(self::PATH.'/'.$this->uid.'/settings/ranking-rules', $rankingRules), partial(Tasks::waitTask(...), $this->http)); } - public function resetRankingRules(): array + public function resetRankingRules(): Task { - return $this->http->delete(self::PATH.'/'.$this->uid.'/settings/ranking-rules'); + return Task::fromArray($this->http->delete(self::PATH.'/'.$this->uid.'/settings/ranking-rules'), partial(Tasks::waitTask(...), $this->http)); } // Settings - Distinct attribute @@ -46,14 +50,14 @@ public function getDistinctAttribute(): ?string /** * @param non-empty-string $distinctAttribute */ - public function updateDistinctAttribute(string $distinctAttribute): array + public function updateDistinctAttribute(string $distinctAttribute): Task { - return $this->http->put(self::PATH.'/'.$this->uid.'/settings/distinct-attribute', $distinctAttribute); + return Task::fromArray($this->http->put(self::PATH.'/'.$this->uid.'/settings/distinct-attribute', $distinctAttribute), partial(Tasks::waitTask(...), $this->http)); } - public function resetDistinctAttribute(): array + public function resetDistinctAttribute(): Task { - return $this->http->delete(self::PATH.'/'.$this->uid.'/settings/distinct-attribute'); + return Task::fromArray($this->http->delete(self::PATH.'/'.$this->uid.'/settings/distinct-attribute'), partial(Tasks::waitTask(...), $this->http)); } // Settings - Searchable attributes @@ -69,14 +73,14 @@ public function getSearchableAttributes(): array /** * @param list $searchableAttributes */ - public function updateSearchableAttributes(array $searchableAttributes): array + public function updateSearchableAttributes(array $searchableAttributes): Task { - return $this->http->put(self::PATH.'/'.$this->uid.'/settings/searchable-attributes', $searchableAttributes); + return Task::fromArray($this->http->put(self::PATH.'/'.$this->uid.'/settings/searchable-attributes', $searchableAttributes), partial(Tasks::waitTask(...), $this->http)); } - public function resetSearchableAttributes(): array + public function resetSearchableAttributes(): Task { - return $this->http->delete(self::PATH.'/'.$this->uid.'/settings/searchable-attributes'); + return Task::fromArray($this->http->delete(self::PATH.'/'.$this->uid.'/settings/searchable-attributes'), partial(Tasks::waitTask(...), $this->http)); } // Settings - Displayed attributes @@ -92,14 +96,14 @@ public function getDisplayedAttributes(): array /** * @param list $displayedAttributes */ - public function updateDisplayedAttributes(array $displayedAttributes): array + public function updateDisplayedAttributes(array $displayedAttributes): Task { - return $this->http->put(self::PATH.'/'.$this->uid.'/settings/displayed-attributes', $displayedAttributes); + return Task::fromArray($this->http->put(self::PATH.'/'.$this->uid.'/settings/displayed-attributes', $displayedAttributes), partial(Tasks::waitTask(...), $this->http)); } - public function resetDisplayedAttributes(): array + public function resetDisplayedAttributes(): Task { - return $this->http->delete(self::PATH.'/'.$this->uid.'/settings/displayed-attributes'); + return Task::fromArray($this->http->delete(self::PATH.'/'.$this->uid.'/settings/displayed-attributes'), partial(Tasks::waitTask(...), $this->http)); } // Settings - Localized attributes @@ -115,14 +119,14 @@ public function getLocalizedAttributes(): ?array /** * @param list, locales: list}> $localizedAttributes */ - public function updateLocalizedAttributes(array $localizedAttributes): array + public function updateLocalizedAttributes(array $localizedAttributes): Task { - return $this->http->put(self::PATH.'/'.$this->uid.'/settings/localized-attributes', $localizedAttributes); + return Task::fromArray($this->http->put(self::PATH.'/'.$this->uid.'/settings/localized-attributes', $localizedAttributes), partial(Tasks::waitTask(...), $this->http)); } - public function resetLocalizedAttributes(): array + public function resetLocalizedAttributes(): Task { - return $this->http->delete(self::PATH.'/'.$this->uid.'/settings/localized-attributes'); + return Task::fromArray($this->http->delete(self::PATH.'/'.$this->uid.'/settings/localized-attributes'), partial(Tasks::waitTask(...), $this->http)); } // Settings - Faceting @@ -139,14 +143,14 @@ public function getFaceting(): array /** * @param array{maxValuesPerFacet?: int, sortFacetValuesBy?: array} $faceting */ - public function updateFaceting(array $faceting): array + public function updateFaceting(array $faceting): Task { - return $this->http->patch(self::PATH.'/'.$this->uid.'/settings/faceting', $faceting); + return Task::fromArray($this->http->patch(self::PATH.'/'.$this->uid.'/settings/faceting', $faceting), partial(Tasks::waitTask(...), $this->http)); } - public function resetFaceting(): array + public function resetFaceting(): Task { - return $this->http->delete(self::PATH.'/'.$this->uid.'/settings/faceting'); + return Task::fromArray($this->http->delete(self::PATH.'/'.$this->uid.'/settings/faceting'), partial(Tasks::waitTask(...), $this->http)); } // Settings - Pagination @@ -162,14 +166,14 @@ public function getPagination(): array /** * @param array{maxTotalHits: positive-int} $pagination */ - public function updatePagination(array $pagination): array + public function updatePagination(array $pagination): Task { - return $this->http->patch(self::PATH.'/'.$this->uid.'/settings/pagination', $pagination); + return Task::fromArray($this->http->patch(self::PATH.'/'.$this->uid.'/settings/pagination', $pagination), partial(Tasks::waitTask(...), $this->http)); } - public function resetPagination(): array + public function resetPagination(): Task { - return $this->http->delete(self::PATH.'/'.$this->uid.'/settings/pagination'); + return Task::fromArray($this->http->delete(self::PATH.'/'.$this->uid.'/settings/pagination'), partial(Tasks::waitTask(...), $this->http)); } // Settings - Stop-words @@ -185,14 +189,14 @@ public function getStopWords(): array /** * @param list $stopWords */ - public function updateStopWords(array $stopWords): array + public function updateStopWords(array $stopWords): Task { - return $this->http->put(self::PATH.'/'.$this->uid.'/settings/stop-words', $stopWords); + return Task::fromArray($this->http->put(self::PATH.'/'.$this->uid.'/settings/stop-words', $stopWords), partial(Tasks::waitTask(...), $this->http)); } - public function resetStopWords(): array + public function resetStopWords(): Task { - return $this->http->delete(self::PATH.'/'.$this->uid.'/settings/stop-words'); + return Task::fromArray($this->http->delete(self::PATH.'/'.$this->uid.'/settings/stop-words'), partial(Tasks::waitTask(...), $this->http)); } // Settings - Synonyms @@ -209,14 +213,14 @@ public function getSynonyms(): array /** * @param array> $synonyms */ - public function updateSynonyms(array $synonyms): array + public function updateSynonyms(array $synonyms): Task { - return $this->http->put(self::PATH.'/'.$this->uid.'/settings/synonyms', new Synonyms($synonyms)); + return Task::fromArray($this->http->put(self::PATH.'/'.$this->uid.'/settings/synonyms', new Synonyms($synonyms)), partial(Tasks::waitTask(...), $this->http)); } - public function resetSynonyms(): array + public function resetSynonyms(): Task { - return $this->http->delete(self::PATH.'/'.$this->uid.'/settings/synonyms'); + return Task::fromArray($this->http->delete(self::PATH.'/'.$this->uid.'/settings/synonyms'), partial(Tasks::waitTask(...), $this->http)); } // Settings - Filterable Attributes @@ -243,14 +247,14 @@ public function getFilterableAttributes(): array * * Note: When attributePatterns contains '_geo', the features field is ignored */ - public function updateFilterableAttributes(array $filterableAttributes): array + public function updateFilterableAttributes(array $filterableAttributes): Task { - return $this->http->put(self::PATH.'/'.$this->uid.'/settings/filterable-attributes', $filterableAttributes); + return Task::fromArray($this->http->put(self::PATH.'/'.$this->uid.'/settings/filterable-attributes', $filterableAttributes), partial(Tasks::waitTask(...), $this->http)); } - public function resetFilterableAttributes(): array + public function resetFilterableAttributes(): Task { - return $this->http->delete(self::PATH.'/'.$this->uid.'/settings/filterable-attributes'); + return Task::fromArray($this->http->delete(self::PATH.'/'.$this->uid.'/settings/filterable-attributes'), partial(Tasks::waitTask(...), $this->http)); } // Settings - Sortable Attributes @@ -266,14 +270,14 @@ public function getSortableAttributes(): array /** * @param list $sortableAttributes */ - public function updateSortableAttributes(array $sortableAttributes): array + public function updateSortableAttributes(array $sortableAttributes): Task { - return $this->http->put(self::PATH.'/'.$this->uid.'/settings/sortable-attributes', $sortableAttributes); + return Task::fromArray($this->http->put(self::PATH.'/'.$this->uid.'/settings/sortable-attributes', $sortableAttributes), partial(Tasks::waitTask(...), $this->http)); } - public function resetSortableAttributes(): array + public function resetSortableAttributes(): Task { - return $this->http->delete(self::PATH.'/'.$this->uid.'/settings/sortable-attributes'); + return Task::fromArray($this->http->delete(self::PATH.'/'.$this->uid.'/settings/sortable-attributes'), partial(Tasks::waitTask(...), $this->http)); } // Settings - Typo Tolerance @@ -300,14 +304,14 @@ public function getTypoTolerance(): array * disableOnAttributes: list * } $typoTolerance */ - public function updateTypoTolerance(array $typoTolerance): array + public function updateTypoTolerance(array $typoTolerance): Task { - return $this->http->patch(self::PATH.'/'.$this->uid.'/settings/typo-tolerance', new TypoTolerance($typoTolerance)); + return Task::fromArray($this->http->patch(self::PATH.'/'.$this->uid.'/settings/typo-tolerance', new TypoTolerance($typoTolerance)), partial(Tasks::waitTask(...), $this->http)); } - public function resetTypoTolerance(): array + public function resetTypoTolerance(): Task { - return $this->http->delete(self::PATH.'/'.$this->uid.'/settings/typo-tolerance'); + return Task::fromArray($this->http->delete(self::PATH.'/'.$this->uid.'/settings/typo-tolerance'), partial(Tasks::waitTask(...), $this->http)); } // Settings - Word dictionary @@ -323,14 +327,14 @@ public function getDictionary(): array /** * @param list $wordDictionary */ - public function updateDictionary(array $wordDictionary): array + public function updateDictionary(array $wordDictionary): Task { - return $this->http->put(self::PATH.'/'.$this->uid.'/settings/dictionary', $wordDictionary); + return Task::fromArray($this->http->put(self::PATH.'/'.$this->uid.'/settings/dictionary', $wordDictionary), partial(Tasks::waitTask(...), $this->http)); } - public function resetDictionary(): array + public function resetDictionary(): Task { - return $this->http->delete(self::PATH.'/'.$this->uid.'/settings/dictionary'); + return Task::fromArray($this->http->delete(self::PATH.'/'.$this->uid.'/settings/dictionary'), partial(Tasks::waitTask(...), $this->http)); } // Settings - Separator tokens @@ -343,14 +347,14 @@ public function getSeparatorTokens(): array /** * @param list $separatorTokens */ - public function updateSeparatorTokens(array $separatorTokens): array + public function updateSeparatorTokens(array $separatorTokens): Task { - return $this->http->put(self::PATH.'/'.$this->uid.'/settings/separator-tokens', $separatorTokens); + return Task::fromArray($this->http->put(self::PATH.'/'.$this->uid.'/settings/separator-tokens', $separatorTokens), partial(Tasks::waitTask(...), $this->http)); } - public function resetSeparatorTokens(): array + public function resetSeparatorTokens(): Task { - return $this->http->delete(self::PATH.'/'.$this->uid.'/settings/separator-tokens'); + return Task::fromArray($this->http->delete(self::PATH.'/'.$this->uid.'/settings/separator-tokens'), partial(Tasks::waitTask(...), $this->http)); } // Settings - Non-Separator tokens @@ -366,14 +370,14 @@ public function getNonSeparatorTokens(): array /** * @param list $nonSeparatorTokens */ - public function updateNonSeparatorTokens(array $nonSeparatorTokens): array + public function updateNonSeparatorTokens(array $nonSeparatorTokens): Task { - return $this->http->put(self::PATH.'/'.$this->uid.'/settings/non-separator-tokens', $nonSeparatorTokens); + return Task::fromArray($this->http->put(self::PATH.'/'.$this->uid.'/settings/non-separator-tokens', $nonSeparatorTokens), partial(Tasks::waitTask(...), $this->http)); } - public function resetNonSeparatorTokens(): array + public function resetNonSeparatorTokens(): Task { - return $this->http->delete(self::PATH.'/'.$this->uid.'/settings/non-separator-tokens'); + return Task::fromArray($this->http->delete(self::PATH.'/'.$this->uid.'/settings/non-separator-tokens'), partial(Tasks::waitTask(...), $this->http)); } // Settings - proximityPrecision @@ -389,31 +393,37 @@ public function getProximityPrecision(): string /** * @param 'byWord'|'byAttribute' $type */ - public function updateProximityPrecision(string $type): array + public function updateProximityPrecision(string $type): Task { - return $this->http->put(self::PATH.'/'.$this->uid.'/settings/proximity-precision', $type); + return Task::fromArray($this->http->put(self::PATH.'/'.$this->uid.'/settings/proximity-precision', $type), partial(Tasks::waitTask(...), $this->http)); } - public function resetProximityPrecision(): array + public function resetProximityPrecision(): Task { - return $this->http->delete(self::PATH.'/'.$this->uid.'/settings/proximity-precision'); + return Task::fromArray($this->http->delete(self::PATH.'/'.$this->uid.'/settings/proximity-precision'), partial(Tasks::waitTask(...), $this->http)); } // Settings - searchCutoffMs + /** + * @return non-negative-int|null + */ public function getSearchCutoffMs(): ?int { return $this->http->get(self::PATH.'/'.$this->uid.'/settings/search-cutoff-ms'); } - public function updateSearchCutoffMs(int $value): array + /** + * @param non-negative-int $value + */ + public function updateSearchCutoffMs(int $value): Task { - return $this->http->put(self::PATH.'/'.$this->uid.'/settings/search-cutoff-ms', $value); + return Task::fromArray($this->http->put(self::PATH.'/'.$this->uid.'/settings/search-cutoff-ms', $value), partial(Tasks::waitTask(...), $this->http)); } - public function resetSearchCutoffMs(): array + public function resetSearchCutoffMs(): Task { - return $this->http->delete(self::PATH.'/'.$this->uid.'/settings/search-cutoff-ms'); + return Task::fromArray($this->http->delete(self::PATH.'/'.$this->uid.'/settings/search-cutoff-ms'), partial(Tasks::waitTask(...), $this->http)); } // Settings - Experimental: Embedders (hybrid search) @@ -423,14 +433,14 @@ public function getEmbedders(): ?array return $this->http->get(self::PATH.'/'.$this->uid.'/settings/embedders'); } - public function updateEmbedders(array $embedders): array + public function updateEmbedders(array $embedders): Task { - return $this->http->patch(self::PATH.'/'.$this->uid.'/settings/embedders', $embedders); + return Task::fromArray($this->http->patch(self::PATH.'/'.$this->uid.'/settings/embedders', $embedders), partial(Tasks::waitTask(...), $this->http)); } - public function resetEmbedders(): array + public function resetEmbedders(): Task { - return $this->http->delete(self::PATH.'/'.$this->uid.'/settings/embedders'); + return Task::fromArray($this->http->delete(self::PATH.'/'.$this->uid.'/settings/embedders'), partial(Tasks::waitTask(...), $this->http)); } // Settings - Facet Search @@ -446,17 +456,17 @@ public function getFacetSearch(): bool /** * @since Meilisearch v1.12.0 */ - public function updateFacetSearch(bool $facetSearch): array + public function updateFacetSearch(bool $facetSearch): Task { - return $this->http->put(self::PATH.'/'.$this->uid.'/settings/facet-search', $facetSearch); + return Task::fromArray($this->http->put(self::PATH.'/'.$this->uid.'/settings/facet-search', $facetSearch), partial(Tasks::waitTask(...), $this->http)); } /** * @since Meilisearch v1.12.0 */ - public function resetFacetSearch(): array + public function resetFacetSearch(): Task { - return $this->http->delete(self::PATH.'/'.$this->uid.'/settings/facet-search'); + return Task::fromArray($this->http->delete(self::PATH.'/'.$this->uid.'/settings/facet-search'), partial(Tasks::waitTask(...), $this->http)); } // Settings - Prefix Search @@ -476,16 +486,16 @@ public function getPrefixSearch(): string * * @since Meilisearch v1.12.0 */ - public function updatePrefixSearch(string $prefixSearch): array + public function updatePrefixSearch(string $prefixSearch): Task { - return $this->http->put(self::PATH.'/'.$this->uid.'/settings/prefix-search', $prefixSearch); + return Task::fromArray($this->http->put(self::PATH.'/'.$this->uid.'/settings/prefix-search', $prefixSearch), partial(Tasks::waitTask(...), $this->http)); } /** * @since Meilisearch v1.12.0 */ - public function resetPrefixSearch(): array + public function resetPrefixSearch(): Task { - return $this->http->delete(self::PATH.'/'.$this->uid.'/settings/prefix-search'); + return Task::fromArray($this->http->delete(self::PATH.'/'.$this->uid.'/settings/prefix-search'), partial(Tasks::waitTask(...), $this->http)); } } diff --git a/src/Endpoints/Delegates/HandlesSnapshots.php b/src/Endpoints/Delegates/HandlesSnapshots.php index c8082a76..b3267e9e 100644 --- a/src/Endpoints/Delegates/HandlesSnapshots.php +++ b/src/Endpoints/Delegates/HandlesSnapshots.php @@ -4,13 +4,14 @@ namespace Meilisearch\Endpoints\Delegates; +use Meilisearch\Contracts\Task; use Meilisearch\Endpoints\Snapshots; trait HandlesSnapshots { protected Snapshots $snapshots; - public function createSnapshot(): array + public function createSnapshot(): Task { return $this->snapshots->create(); } diff --git a/src/Endpoints/Delegates/HandlesSystem.php b/src/Endpoints/Delegates/HandlesSystem.php index 7abb2937..b920a9f4 100644 --- a/src/Endpoints/Delegates/HandlesSystem.php +++ b/src/Endpoints/Delegates/HandlesSystem.php @@ -4,6 +4,7 @@ namespace Meilisearch\Endpoints\Delegates; +use Meilisearch\Contracts\Task; use Meilisearch\Endpoints\Health; use Meilisearch\Endpoints\Stats; use Meilisearch\Endpoints\TenantToken; @@ -47,7 +48,7 @@ public function generateTenantToken(string $apiKeyUid, $searchRules, array $opti return $this->tenantToken->generateTenantToken($apiKeyUid, $searchRules, $options); } - public function swapIndexes(array $indexes): array + public function swapIndexes(array $indexes): Task { $options = array_map(static fn ($data) => ['indexes' => $data], $indexes); diff --git a/src/Endpoints/Delegates/HandlesTasks.php b/src/Endpoints/Delegates/HandlesTasks.php index efdb6432..56f701aa 100644 --- a/src/Endpoints/Delegates/HandlesTasks.php +++ b/src/Endpoints/Delegates/HandlesTasks.php @@ -6,16 +6,16 @@ use Meilisearch\Contracts\CancelTasksQuery; use Meilisearch\Contracts\DeleteTasksQuery; +use Meilisearch\Contracts\Task; use Meilisearch\Contracts\TasksQuery; use Meilisearch\Contracts\TasksResults; use Meilisearch\Endpoints\Tasks; -use Meilisearch\Exceptions\TimeOutException; trait HandlesTasks { protected Tasks $tasks; - public function getTask($uid): array + public function getTask(int $uid): Task { return $this->tasks->get($uid); } @@ -29,29 +29,13 @@ public function getTasks(?TasksQuery $options = null): TasksResults return new TasksResults($response); } - public function deleteTasks(?DeleteTasksQuery $options = null): array + public function deleteTasks(?DeleteTasksQuery $options = null): Task { return $this->tasks->deleteTasks($options); } - public function cancelTasks(?CancelTasksQuery $options = null): array + public function cancelTasks(?CancelTasksQuery $options = null): Task { return $this->tasks->cancelTasks($options); } - - /** - * @throws TimeOutException - */ - public function waitForTask($uid, int $timeoutInMs = 5000, int $intervalInMs = 50): array - { - return $this->tasks->waitTask($uid, $timeoutInMs, $intervalInMs); - } - - /** - * @throws TimeOutException - */ - public function waitForTasks($uids, int $timeoutInMs = 5000, int $intervalInMs = 50): array - { - return $this->tasks->waitTasks($uids, $timeoutInMs, $intervalInMs); - } } diff --git a/src/Endpoints/Delegates/TasksQueryTrait.php b/src/Endpoints/Delegates/TasksQueryTrait.php index 058f5bbc..00a545e5 100644 --- a/src/Endpoints/Delegates/TasksQueryTrait.php +++ b/src/Endpoints/Delegates/TasksQueryTrait.php @@ -148,7 +148,7 @@ protected function baseArray(): array private function formatDate(?\DateTimeInterface $date): ?string { - return null !== $date ? $date->format(\DateTimeInterface::RFC3339) : null; + return $date?->format(\DateTimeInterface::RFC3339); } private function formatArray(?array $array): ?string diff --git a/src/Endpoints/Dumps.php b/src/Endpoints/Dumps.php index 1d662896..aee92e02 100644 --- a/src/Endpoints/Dumps.php +++ b/src/Endpoints/Dumps.php @@ -5,13 +5,16 @@ namespace Meilisearch\Endpoints; use Meilisearch\Contracts\Endpoint; +use Meilisearch\Contracts\Task; + +use function Meilisearch\partial; class Dumps extends Endpoint { protected const PATH = '/dumps'; - public function create(): array + public function create(): Task { - return $this->http->post(self::PATH); + return Task::fromArray($this->http->post(self::PATH), partial(Tasks::waitTask(...), $this->http)); } } diff --git a/src/Endpoints/Indexes.php b/src/Endpoints/Indexes.php index 3af12493..0aba5c3c 100644 --- a/src/Endpoints/Indexes.php +++ b/src/Endpoints/Indexes.php @@ -11,6 +11,7 @@ use Meilisearch\Contracts\IndexesQuery; use Meilisearch\Contracts\IndexesResults; use Meilisearch\Contracts\SimilarDocumentsQuery; +use Meilisearch\Contracts\Task; use Meilisearch\Contracts\TasksQuery; use Meilisearch\Contracts\TasksResults; use Meilisearch\Endpoints\Delegates\HandlesDocuments; @@ -21,6 +22,8 @@ use Meilisearch\Search\SearchResult; use Meilisearch\Search\SimilarDocumentsSearchResult; +use function Meilisearch\partial; + class Indexes extends Endpoint { use HandlesDocuments; @@ -72,11 +75,11 @@ protected function fill(array $attributes): self /** * @throws \Exception|ApiException */ - public function create(string $uid, array $options = []): array + public function create(string $uid, array $options = []): Task { $options['uid'] = $uid; - return $this->http->post(self::PATH, $options); + return Task::fromArray($this->http->post(self::PATH, $options), partial(\Closure::fromCallable([Tasks::class, 'waitTask']), $this->http)); } public function all(?IndexesQuery $options = null): IndexesResults @@ -136,37 +139,40 @@ public function fetchInfo(): self return $this->fill($response); } - public function update($body): array + public function update(array $body): Task { - return $this->http->patch(self::PATH.'/'.$this->uid, $body); + return Task::fromArray($this->http->patch(self::PATH.'/'.$this->uid, $body), partial(Tasks::waitTask(...), $this->http)); } - public function delete(): array + public function delete(): Task { - return $this->http->delete(self::PATH.'/'.$this->uid) ?? []; + $response = $this->http->delete(self::PATH.'/'.$this->uid); + \assert(null !== $response); + + return Task::fromArray($response, partial(Tasks::waitTask(...), $this->http)); } /** * @param array $indexes */ - public function swapIndexes(array $indexes): array + public function swapIndexes(array $indexes): Task { - return $this->http->post('/swap-indexes', $indexes); + return Task::fromArray($this->http->post('/swap-indexes', $indexes), partial(Tasks::waitTask(...), $this->http)); } // Tasks - public function getTask($uid): array + public function getTask(int $uid): Task { - return $this->http->get('/tasks/'.$uid); + return Task::fromArray($this->http->get('/tasks/'.$uid), partial(Tasks::waitTask(...), $this->http)); } public function getTasks(?TasksQuery $options = null): TasksResults { $options = $options ?? new TasksQuery(); - if (\count($options->getIndexUids()) > 0) { - $options->setIndexUids(array_merge([$this->uid], $options->getIndexUids())); + if ([] !== $options->getIndexUids()) { + $options->setIndexUids([$this->uid, ...$options->getIndexUids()]); } else { $options->setIndexUids([$this->uid]); } @@ -179,11 +185,9 @@ public function getTasks(?TasksQuery $options = null): TasksResults // Search /** - * @return SearchResult|array - * * @phpstan-return ($options is array{raw: true|non-falsy-string|positive-int} ? array : SearchResult) */ - public function search(?string $query, array $searchParams = [], array $options = []) + public function search(?string $query, array $searchParams = [], array $options = []): SearchResult|array { $result = $this->rawSearch($query, $searchParams); @@ -246,14 +250,14 @@ public function getSettings(): array ->getIterator()->getArrayCopy(); } - public function updateSettings($settings): array + public function updateSettings($settings): Task { - return $this->http->patch(self::PATH.'/'.$this->uid.'/settings', $settings); + return Task::fromArray($this->http->patch(self::PATH.'/'.$this->uid.'/settings', $settings), partial(Tasks::waitTask(...), $this->http)); } - public function resetSettings(): array + public function resetSettings(): Task { - return $this->http->delete(self::PATH.'/'.$this->uid.'/settings'); + return Task::fromArray($this->http->delete(self::PATH.'/'.$this->uid.'/settings'), partial(Tasks::waitTask(...), $this->http)); } /** diff --git a/src/Endpoints/Keys.php b/src/Endpoints/Keys.php index bfe09efe..19e084b1 100644 --- a/src/Endpoints/Keys.php +++ b/src/Endpoints/Keys.php @@ -95,7 +95,7 @@ protected function createDate($attribute): ?\DateTimeInterface return null; } - if (false === strpos($attribute, '.')) { + if (!str_contains($attribute, '.')) { $date = \DateTimeImmutable::createFromFormat(\DateTimeInterface::ATOM, $attribute); } else { $attribute = preg_replace('/(\.\d{6})\d+/', '$1', $attribute, 1); @@ -150,7 +150,10 @@ public function getUpdatedAt(): ?\DateTimeInterface return $this->updatedAt; } - public function get($keyOrUid): self + /** + * @param non-empty-string $keyOrUid + */ + public function get(string $keyOrUid): self { $response = $this->http->get(self::PATH.'/'.$keyOrUid); @@ -188,6 +191,9 @@ public function create(array $options = []): self return $this->fill($response); } + /** + * @param non-empty-string $keyOrUid + */ public function update(string $keyOrUid, array $options = []): self { $data = array_intersect_key($options, array_flip(['description', 'name'])); @@ -196,6 +202,9 @@ public function update(string $keyOrUid, array $options = []): self return $this->fill($response); } + /** + * @param non-empty-string $keyOrUid + */ public function delete(string $keyOrUid): array { return $this->http->delete(self::PATH.'/'.$keyOrUid) ?? []; diff --git a/src/Endpoints/Snapshots.php b/src/Endpoints/Snapshots.php index fa559176..96db3fb3 100644 --- a/src/Endpoints/Snapshots.php +++ b/src/Endpoints/Snapshots.php @@ -5,13 +5,16 @@ namespace Meilisearch\Endpoints; use Meilisearch\Contracts\Endpoint; +use Meilisearch\Contracts\Task; + +use function Meilisearch\partial; class Snapshots extends Endpoint { protected const PATH = '/snapshots'; - public function create(): array + public function create(): Task { - return $this->http->post(self::PATH); + return Task::fromArray($this->http->post(self::PATH), partial(Tasks::waitTask(...), $this->http)); } } diff --git a/src/Endpoints/Tasks.php b/src/Endpoints/Tasks.php index 1d6d2b3f..8d0814d2 100644 --- a/src/Endpoints/Tasks.php +++ b/src/Endpoints/Tasks.php @@ -7,48 +7,55 @@ use Meilisearch\Contracts\CancelTasksQuery; use Meilisearch\Contracts\DeleteTasksQuery; use Meilisearch\Contracts\Endpoint; +use Meilisearch\Contracts\Http; +use Meilisearch\Contracts\Task; use Meilisearch\Exceptions\TimeOutException; +use function Meilisearch\partial; + class Tasks extends Endpoint { protected const PATH = '/tasks'; - public function get($taskUid): array + public function get(int $taskUid): Task { - return $this->http->get(self::PATH.'/'.$taskUid); + return Task::fromArray($this->http->get(self::PATH.'/'.$taskUid), partial(self::waitTask(...), $this->http)); } + // @todo: must return array public function all(array $query = []): array { return $this->http->get(self::PATH.'/', $query); } - public function cancelTasks(?CancelTasksQuery $options): array + public function cancelTasks(?CancelTasksQuery $options): Task { $options = $options ?? new CancelTasksQuery(); - return $this->http->post('/tasks/cancel', null, $options->toArray()); + return Task::fromArray($this->http->post('/tasks/cancel', null, $options->toArray()), partial(self::waitTask(...), $this->http)); } - public function deleteTasks(?DeleteTasksQuery $options): array + public function deleteTasks(?DeleteTasksQuery $options): Task { $options = $options ?? new DeleteTasksQuery(); - return $this->http->delete(self::PATH, $options->toArray()); + return Task::fromArray($this->http->delete(self::PATH, $options->toArray()), partial(self::waitTask(...), $this->http)); } /** + * @internal + * * @throws TimeOutException */ - public function waitTask($taskUid, int $timeoutInMs, int $intervalInMs): array + public static function waitTask(Http $http, int $taskUid, int $timeoutInMs, int $intervalInMs): Task { $timeoutTemp = 0; while ($timeoutInMs > $timeoutTemp) { - $res = $this->get($taskUid); + $task = Task::fromArray($http->get(self::PATH.'/'.$taskUid), partial(self::waitTask(...), $http)); - if ('enqueued' !== $res['status'] && 'processing' !== $res['status']) { - return $res; + if ($task->isFinished()) { + return $task; } $timeoutTemp += $intervalInMs; @@ -57,18 +64,4 @@ public function waitTask($taskUid, int $timeoutInMs, int $intervalInMs): array throw new TimeOutException(); } - - /** - * @throws TimeOutException - */ - public function waitTasks(array $taskUids, int $timeoutInMs, int $intervalInMs): array - { - $tasks = []; - - foreach ($taskUids as $taskUid) { - $tasks[] = $this->waitTask($taskUid, $timeoutInMs, $intervalInMs); - } - - return $tasks; - } } diff --git a/src/Endpoints/TenantToken.php b/src/Endpoints/TenantToken.php index 4bf4f9c0..9158e912 100644 --- a/src/Endpoints/TenantToken.php +++ b/src/Endpoints/TenantToken.php @@ -18,12 +18,12 @@ private function base64url_encode(string $data): string /** * @param array{apiKey?: ?string, expiresAt?: ?\DateTimeInterface} $options */ - private function validateTenantTokenArguments($searchRules, array $options = []): void + private function validateTenantTokenArguments(array|object $searchRules, array $options = []): void { if (!isset($options['apiKey']) || ('' === $options['apiKey'] || \strlen($options['apiKey']) <= 8)) { throw InvalidArgumentException::emptyArgument('api key'); } - if ((!\is_array($searchRules) || [] === $searchRules) && !\is_object($searchRules)) { + if ([] === $searchRules) { throw InvalidArgumentException::emptyArgument('search rules'); } if (isset($options['expiresAt']) && new \DateTimeImmutable() > $options['expiresAt']) { @@ -40,7 +40,7 @@ private function validateTenantTokenArguments($searchRules, array $options = []) * * @param array{apiKey?: ?string, expiresAt?: ?\DateTimeInterface} $options */ - public function generateTenantToken(string $uid, $searchRules, array $options = []): string + public function generateTenantToken(string $uid, array|object $searchRules, array $options = []): string { if (!isset($options['apiKey']) || '' === $options['apiKey']) { $options['apiKey'] = $this->apiKey; diff --git a/src/Exceptions/ApiException.php b/src/Exceptions/ApiException.php index 2e0f21c7..0307f14d 100644 --- a/src/Exceptions/ApiException.php +++ b/src/Exceptions/ApiException.php @@ -13,11 +13,11 @@ class ApiException extends \Exception implements ExceptionInterface public ?string $errorCode; public ?string $errorType; public ?string $errorLink; - public $httpBody; + public mixed $httpBody; public const HINT_MESSAGE = "Hint: It might not be working because maybe you're not up to date with the Meilisearch version that `%s` call requires."; - public function __construct(ResponseInterface $response, $httpBody, $previous = null) + public function __construct(ResponseInterface $response, mixed $httpBody, ?\Throwable $previous = null) { $this->httpBody = $httpBody; $this->httpStatus = $response->getStatusCode(); @@ -29,7 +29,7 @@ public function __construct(ResponseInterface $response, $httpBody, $previous = parent::__construct($this->message, $this->httpStatus, $previous); } - public function __toString() + public function __toString(): string { $base = 'Meilisearch ApiException: Http Status: '.$this->httpStatus; @@ -88,7 +88,7 @@ private function getErrorLinkFromHttpBody(): ?string return null; } - public static function rethrowWithHint(\Exception $e, string $methodName) + public static function rethrowWithHint(\Throwable $e, string $methodName): \Exception { return new \Exception(\sprintf(self::HINT_MESSAGE, $methodName), 0, $e); } diff --git a/src/Exceptions/CommunicationException.php b/src/Exceptions/CommunicationException.php index e4fc8a97..7b5504b7 100644 --- a/src/Exceptions/CommunicationException.php +++ b/src/Exceptions/CommunicationException.php @@ -6,7 +6,7 @@ class CommunicationException extends \Exception implements ExceptionInterface { - public function __toString() + public function __toString(): string { return 'Meilisearch CommunicationException: '.$this->getMessage(); } diff --git a/src/Exceptions/InvalidArgumentException.php b/src/Exceptions/InvalidArgumentException.php index 04b31f15..90a62206 100644 --- a/src/Exceptions/InvalidArgumentException.php +++ b/src/Exceptions/InvalidArgumentException.php @@ -6,15 +6,6 @@ final class InvalidArgumentException extends \Exception implements ExceptionInterface { - public static function invalidType(string $argumentName, array $validTypes): self - { - return new self( - \sprintf('Argument "%s" is not a valid type! Please provide an argument that is of type: "%s"', $argumentName, implode('","', $validTypes)), - 400, - null - ); - } - public static function emptyArgument(string $argumentName): self { return new self( diff --git a/src/Exceptions/InvalidResponseBodyException.php b/src/Exceptions/InvalidResponseBodyException.php index 5eff72fd..6cc06d86 100644 --- a/src/Exceptions/InvalidResponseBodyException.php +++ b/src/Exceptions/InvalidResponseBodyException.php @@ -8,8 +8,8 @@ class InvalidResponseBodyException extends \Exception implements ExceptionInterface { - public $httpStatus = 0; - public $httpBody; + public int $httpStatus = 0; + public mixed $httpBody; public $message; public function __construct(ResponseInterface $response, $httpBody, $previous = null) @@ -21,7 +21,7 @@ public function __construct(ResponseInterface $response, $httpBody, $previous = parent::__construct($this->message, $this->httpStatus, $previous); } - public function __toString() + public function __toString(): string { $base = 'Meilisearch InvalidResponseBodyException: Http Status: '.$this->httpStatus; diff --git a/src/Exceptions/LogicException.php b/src/Exceptions/LogicException.php new file mode 100644 index 00000000..9d1fda30 --- /dev/null +++ b/src/Exceptions/LogicException.php @@ -0,0 +1,9 @@ +message, $this->code, $previous); } - public function __toString() + public function __toString(): string { $base = 'Meilisearch TimeOutException: Code: '.$this->code; if ($this->message) { return $base.' - Message: '.$this->message; - } else { - return $base; } + + return $base; } } diff --git a/src/Http/Client.php b/src/Http/Client.php index 319c7d59..e798ab3c 100644 --- a/src/Http/Client.php +++ b/src/Http/Client.php @@ -45,7 +45,7 @@ public function __construct( ?ClientInterface $httpClient = null, ?RequestFactoryInterface $reqFactory = null, array $clientAgents = [], - ?StreamFactoryInterface $streamFactory = null + ?StreamFactoryInterface $streamFactory = null, ) { $this->baseUrl = $url; $this->http = $httpClient ?? Psr18ClientDiscovery::find(); @@ -159,7 +159,7 @@ private function execute(RequestInterface $request, array $headers = []) private function buildQueryString(array $queryParams = []): string { - return \count($queryParams) > 0 ? '?'.http_build_query($queryParams) : ''; + return [] !== $queryParams ? '?'.http_build_query($queryParams) : ''; } /** @@ -195,10 +195,6 @@ private function parseResponse(ResponseInterface $response) */ private function isJSONResponse(array $headerValues): bool { - $filteredHeaders = array_filter($headerValues, static function (string $headerValue) { - return false !== strpos($headerValue, 'application/json'); - }); - - return \count($filteredHeaders) > 0; + return [] !== array_filter($headerValues, static fn (string $v) => str_contains($v, 'application/json')); } } diff --git a/src/Http/Serialize/Json.php b/src/Http/Serialize/Json.php index 0e66cdce..8bf9f56a 100644 --- a/src/Http/Serialize/Json.php +++ b/src/Http/Serialize/Json.php @@ -6,12 +6,12 @@ class Json implements SerializerInterface { - public function serialize($data) + public function serialize(mixed $data): string { return json_encode($data, JSON_THROW_ON_ERROR); } - public function unserialize(string $string) + public function unserialize(mixed $string): mixed { return json_decode($string, true, 512, \JSON_BIGINT_AS_STRING | \JSON_THROW_ON_ERROR); } diff --git a/src/Http/Serialize/SerializerInterface.php b/src/Http/Serialize/SerializerInterface.php index 8e076019..941f25e6 100644 --- a/src/Http/Serialize/SerializerInterface.php +++ b/src/Http/Serialize/SerializerInterface.php @@ -11,11 +11,9 @@ interface SerializerInterface * * @param string|int|float|bool|array|null $data * - * @return string|bool - * * @throws \JsonException */ - public function serialize($data); + public function serialize(mixed $data): string; /** * Unserialize the given string. @@ -24,5 +22,5 @@ public function serialize($data); * * @throws \JsonException */ - public function unserialize(string $string); + public function unserialize(mixed $string): mixed; } diff --git a/src/Search/FacetSearchResult.php b/src/Search/FacetSearchResult.php index 24d3774d..ab85b5c1 100644 --- a/src/Search/FacetSearchResult.php +++ b/src/Search/FacetSearchResult.php @@ -47,7 +47,7 @@ public function toArray(): array public function toJSON(): string { - return json_encode($this->toArray(), JSON_PRETTY_PRINT); + return json_encode($this->toArray(), JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR); } public function getIterator(): \ArrayIterator diff --git a/src/Search/SearchResult.php b/src/Search/SearchResult.php index 3873a72b..449833cf 100644 --- a/src/Search/SearchResult.php +++ b/src/Search/SearchResult.php @@ -245,7 +245,7 @@ public function toArray(): array public function toJSON(): string { - return json_encode($this->toArray(), JSON_PRETTY_PRINT); + return json_encode($this->toArray(), JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR); } public function getIterator(): \ArrayIterator diff --git a/src/functions.php b/src/functions.php new file mode 100644 index 00000000..23bb6d1a --- /dev/null +++ b/src/functions.php @@ -0,0 +1,15 @@ +getTaskUid()); + self::assertSame('documents', $task->getIndexUid()); + self::assertSame(TaskStatus::Failed, $task->getStatus()); + self::assertSame(TaskType::IndexCreation, $task->getType()); + self::assertEquals(new \DateTimeImmutable('2025-04-09T10:28:12.236789'), $task->getEnqueuedAt()); + self::assertEquals(new \DateTimeImmutable('2025-04-09T10:28:12.555566+0000'), $task->getStartedAt()); + self::assertEquals(new \DateTimeImmutable('2025-04-09T10:28:12.666677+0000'), $task->getFinishedAt()); + self::assertSame('PT0.184408715S', $task->getDuration()); + self::assertSame(123, $task->getCanceledBy()); + self::assertSame(666, $task->getBatchUid()); + self::assertEquals(new IndexCreationDetails('custom_id'), $task->getDetails()); + self::assertEquals(new TaskError( + 'Index `documents` not found.', + 'index_not_found', + 'invalid_request', + 'https://docs.meilisearch.com/errors#index_not_found', + ), $task->getError()); + } + + public function testCreateEnqueuedTask(): void + { + $task = new Task( + taskUid: 1, + indexUid: 'documents', + status: TaskStatus::Enqueued, + type: TaskType::IndexCreation, + enqueuedAt: new \DateTimeImmutable('2025-04-09 10:28:12.236789'), + ); + + self::assertSame(1, $task->getTaskUid()); + self::assertSame('documents', $task->getIndexUid()); + self::assertSame(TaskStatus::Enqueued, $task->getStatus()); + self::assertSame(TaskType::IndexCreation, $task->getType()); + self::assertEquals(new \DateTimeImmutable('2025-04-09T10:28:12.236789+0000'), $task->getEnqueuedAt()); + self::assertNull($task->getStartedAt()); + self::assertNull($task->getFinishedAt()); + self::assertNull($task->getDuration()); + self::assertNull($task->getCanceledBy()); + self::assertNull($task->getBatchUid()); + self::assertNull($task->getDetails()); + self::assertNull($task->getError()); + } + + public function testWait(): void + { + $await = static function () { + return MockTask::create(TaskType::IndexCreation, status: TaskStatus::Succeeded); + }; + $task = MockTask::create(TaskType::IndexCreation, await: $await(...)); + $completedTask = $task->wait(); + + self::assertNotSame($task, $completedTask); + self::assertSame(TaskStatus::Succeeded, $completedTask->getStatus()); + } + + #[TestWith([TaskStatus::Succeeded])] + #[TestWith([TaskStatus::Failed])] + #[TestWith([TaskStatus::Canceled])] + public function testWaitReturnsImmediatelyIfTaskIsAlreadyFinished(TaskStatus $status): void + { + $task = MockTask::create(TaskType::IndexCreation, status: $status); + + self::assertSame($task, $task->wait()); + } + + public function testWaitThrowsWithoutFunction(): void + { + $task = MockTask::create(TaskType::IndexCreation); + + $this->expectException(LogicException::class); + $this->expectExceptionMessage('Cannot wait for task because wait function is not provided.'); + + $task->wait(); + } +} diff --git a/tests/Endpoints/BatchesTest.php b/tests/Endpoints/BatchesTest.php index d29eec86..828256d3 100644 --- a/tests/Endpoints/BatchesTest.php +++ b/tests/Endpoints/BatchesTest.php @@ -36,7 +36,7 @@ public function testGetAllBatchesWithIndexUidFilters(): void public function testGetAllBatchesWithTasksFilters(): void { $tasks = $this->client->getTasks(new TasksQuery())->getResults(); - $response = $this->client->getBatches((new BatchesQuery())->setUids([$tasks[0]['uid']])); + $response = $this->client->getBatches((new BatchesQuery())->setUids([$tasks[0]->getTaskUid()])); self::assertGreaterThan(0, $response->getTotal()); } diff --git a/tests/Endpoints/ClientTest.php b/tests/Endpoints/ClientTest.php index e5c8c714..b0d2ca49 100644 --- a/tests/Endpoints/ClientTest.php +++ b/tests/Endpoints/ClientTest.php @@ -88,14 +88,13 @@ public function testCreateIndexWithUidInOptions(): void self::assertSame('ObjectId', $index->getPrimaryKey()); } - public function testgetIndexes(): void + public function testGetIndexes(): void { $booksIndex1 = $this->safeIndexName('books-1'); $booksIndex2 = $this->safeIndexName('books-2'); - $response = $this->client->createIndex($booksIndex1); - $this->client->waitForTask($response['taskUid']); - $response = $this->client->createIndex($booksIndex2); - $this->client->waitForTask($response['taskUid']); + + $this->client->createIndex($booksIndex1)->wait(); + $this->client->createIndex($booksIndex2)->wait(); $indexes = $this->client->getIndexes(); @@ -116,9 +115,8 @@ public function testUpdateIndex(): void $indexName = $this->safeIndexName('books-1'); $this->createEmptyIndex($indexName); - $response = $this->client->updateIndex($indexName, ['primaryKey' => 'id']); - $this->client->waitForTask($response['taskUid']); - $index = $this->client->getIndex($response['indexUid']); + $task = $this->client->updateIndex($indexName, ['primaryKey' => 'id'])->wait(); + $index = $this->client->getIndex($task->getIndexUid()); self::assertSame('id', $index->getPrimaryKey()); self::assertSame($indexName, $index->getUid()); @@ -131,8 +129,7 @@ public function testDeleteIndex(): void $response = $this->client->getIndexes(); self::assertCount(1, $response); - $response = $this->client->deleteIndex('index'); - $this->client->waitForTask($response['taskUid']); + $this->client->deleteIndex('index')->wait(); $this->expectException(ApiException::class); $index = $this->client->getIndex('index'); diff --git a/tests/Endpoints/DocumentsTest.php b/tests/Endpoints/DocumentsTest.php index 5df160a9..a18589ee 100644 --- a/tests/Endpoints/DocumentsTest.php +++ b/tests/Endpoints/DocumentsTest.php @@ -6,12 +6,16 @@ use Meilisearch\Contracts\DocumentsQuery; use Meilisearch\Contracts\Http; +use Meilisearch\Contracts\Task; +use Meilisearch\Contracts\TaskDetails\DocumentAdditionOrUpdateDetails; +use Meilisearch\Contracts\TaskStatus; +use Meilisearch\Contracts\TaskType; use Meilisearch\Endpoints\Indexes; use Meilisearch\Exceptions\ApiException; -use Meilisearch\Exceptions\InvalidArgumentException; use Meilisearch\Exceptions\InvalidResponseBodyException; use Meilisearch\Http\Client; use Psr\Http\Message\ResponseInterface; +use Tests\MockTask; use Tests\TestCase; final class DocumentsTest extends TestCase @@ -19,11 +23,8 @@ final class DocumentsTest extends TestCase public function testAddDocuments(): void { $index = $this->createEmptyIndex($this->safeIndexName('movies')); - $promise = $index->addDocuments(self::DOCUMENTS); + $task = $index->addDocuments(self::DOCUMENTS)->wait(); - $this->assertIsValidPromise($promise); - - $index->waitForTask($promise['taskUid']); $response = $index->getDocuments(); self::assertCount(\count(self::DOCUMENTS), $response); } @@ -31,13 +32,12 @@ public function testAddDocuments(): void public function testAddDocumentsInBatches(): void { $index = $this->createEmptyIndex($this->safeIndexName('movies')); - $promises = $index->addDocumentsInBatches(self::DOCUMENTS, 2); + $tasks = $index->addDocumentsInBatches(self::DOCUMENTS, 2); - self::assertCount(4, $promises); + self::assertCount(4, $tasks); - foreach ($promises as $promise) { - $this->assertIsValidPromise($promise); - $index->waitForTask($promise['taskUid']); + foreach ($tasks as $task) { + $task->wait(); } $response = $index->getDocuments(); @@ -53,10 +53,7 @@ public function testAddDocumentWithSpecialChars(): void ]; $index = $this->createEmptyIndex($this->safeIndexName('movies')); - $promise = $index->addDocuments($documents); - - $this->assertIsValidPromise($promise); - $index->waitForTask($promise['taskUid']); + $task = $index->addDocuments($documents)->wait(); $response = $index->getDocuments(); self::assertCount(\count($documents), $response); @@ -75,14 +72,11 @@ public function testAddDocumentsCsv(): void $documentCsv = fread($fileCsv, filesize('./tests/datasets/songs.csv')); fclose($fileCsv); - $promise = $index->addDocumentsCsv($documentCsv); - - $this->assertIsValidPromise($promise); - - $update = $index->waitForTask($promise['taskUid']); + $task = $index->addDocumentsCsv($documentCsv)->wait(); - self::assertSame('succeeded', $update['status']); - self::assertNotSame(0, $update['details']['receivedDocuments']); + self::assertSame(TaskStatus::Succeeded, $task->getStatus()); + self::assertInstanceOf(DocumentAdditionOrUpdateDetails::class, $details = $task->getDetails()); + self::assertNotSame(0, $details->receivedDocuments); $response = $index->getDocuments(); self::assertCount(20, $response); @@ -94,14 +88,11 @@ public function testAddDocumentsCsvWithCustomSeparator(): void $csv = file_get_contents('./tests/datasets/songs-custom-separator.csv', true); - $promise = $index->addDocumentsCsv($csv, null, '|'); + $task = $index->addDocumentsCsv($csv, null, '|')->wait(); - $this->assertIsValidPromise($promise); - - $update = $index->waitForTask($promise['taskUid']); - - self::assertSame('succeeded', $update['status']); - self::assertSame(6, $update['details']['receivedDocuments']); + self::assertSame(TaskStatus::Succeeded, $task->getStatus()); + self::assertInstanceOf(DocumentAdditionOrUpdateDetails::class, $details = $task->getDetails()); + self::assertSame(6, $details->receivedDocuments); $documents = $index->getDocuments()->getResults(); self::assertSame('Teenage Neon Jungle', $documents[4]['album']); @@ -116,14 +107,11 @@ public function testAddDocumentsJson(): void $documentJson = fread($fileJson, filesize('./tests/datasets/small_movies.json')); fclose($fileJson); - $promise = $index->addDocumentsJson($documentJson); - - $this->assertIsValidPromise($promise); - - $update = $index->waitForTask($promise['taskUid']); + $task = $index->addDocumentsJson($documentJson)->wait(); - self::assertSame('succeeded', $update['status']); - self::assertNotSame(0, $update['details']['receivedDocuments']); + self::assertSame(TaskStatus::Succeeded, $task->getStatus()); + self::assertInstanceOf(DocumentAdditionOrUpdateDetails::class, $details = $task->getDetails()); + self::assertNotSame(0, $details->receivedDocuments); $response = $index->getDocuments(); self::assertCount(20, $response); @@ -137,14 +125,11 @@ public function testAddDocumentsNdJson(): void $documentNdJson = fread($fileNdJson, filesize('./tests/datasets/songs.ndjson')); fclose($fileNdJson); - $promise = $index->addDocumentsNdjson($documentNdJson); + $task = $index->addDocumentsNdjson($documentNdJson)->wait(); - $this->assertIsValidPromise($promise); - - $update = $index->waitForTask($promise['taskUid']); - - self::assertSame('succeeded', $update['status']); - self::assertNotSame(0, $update['details']['receivedDocuments']); + self::assertSame(TaskStatus::Succeeded, $task->getStatus()); + self::assertInstanceOf(DocumentAdditionOrUpdateDetails::class, $details = $task->getDetails()); + self::assertNotSame(0, $details->receivedDocuments); $response = $index->getDocuments(); self::assertCount(20, $response); @@ -164,12 +149,12 @@ public function testCannotAddDocumentWhenJsonEncodingFails(): void public function testGetSingleDocumentWithIntegerDocumentId(): void { $index = $this->createEmptyIndex($this->safeIndexName('movies')); - $response = $index->addDocuments(self::DOCUMENTS); - $index->waitForTask($response['taskUid']); + + $index->addDocuments(self::DOCUMENTS)->wait(); + $doc = $this->findDocumentWithId(self::DOCUMENTS, 4); $response = $index->getDocument($doc['id']); - self::assertIsArray($response); self::assertSame($doc['id'], $response['id']); self::assertSame($doc['title'], $response['title']); } @@ -177,12 +162,12 @@ public function testGetSingleDocumentWithIntegerDocumentId(): void public function testGetSingleDocumentWithFields(): void { $index = $this->createEmptyIndex($this->safeIndexName('movies')); - $response = $index->addDocuments(self::DOCUMENTS); - $index->waitForTask($response['taskUid']); + + $index->addDocuments(self::DOCUMENTS)->wait(); + $doc = $this->findDocumentWithId(self::DOCUMENTS, 4); $response = $index->getDocument($doc['id'], ['title']); - self::assertIsArray($response); self::assertSame($doc['title'], $response['title']); self::assertArrayNotHasKey('id', $response); } @@ -190,20 +175,21 @@ public function testGetSingleDocumentWithFields(): void public function testGetSingleDocumentWithStringDocumentId(): void { $stringDocumentId = 'myUniqueId'; + $index = $this->createEmptyIndex($this->safeIndexName('movies')); - $addDocumentResponse = $index->addDocuments([['id' => $stringDocumentId]]); - $index->waitForTask($addDocumentResponse['taskUid']); + $index->addDocuments([['id' => $stringDocumentId]])->wait(); + $response = $index->getDocument($stringDocumentId); - self::assertIsArray($response); self::assertSame($stringDocumentId, $response['id']); } public function testGetMultipleDocumentsByIds(): void { $index = $this->createEmptyIndex($this->safeIndexName('movies')); - $response = $index->addDocuments(self::DOCUMENTS); - $index->waitForTask($response['taskUid']); + + $index->addDocuments(self::DOCUMENTS)->wait(); + $documentIds = [1, 2]; $response = $index->getDocuments((new DocumentsQuery())->setIds($documentIds)); @@ -216,17 +202,16 @@ public function testGetMultipleDocumentsByIds(): void public function testReplaceDocuments(): void { $index = $this->createEmptyIndex($this->safeIndexName('movies')); - $response = $index->addDocuments(self::DOCUMENTS); - $index->waitForTask($response['taskUid']); + + $task = $index->addDocuments(self::DOCUMENTS)->wait(); + $replacement = [ 'id' => 2, 'title' => 'The Red And The Black', ]; - $response = $index->addDocuments([$replacement]); - $this->assertIsValidPromise($response); + $task = $index->addDocuments([$replacement])->wait(); - $index->waitForTask($response['taskUid']); $response = $index->getDocument($replacement['id']); self::assertSame($replacement['id'], $response['id']); @@ -239,17 +224,15 @@ public function testReplaceDocuments(): void public function testUpdateDocuments(): void { $index = $this->createEmptyIndex($this->safeIndexName('movies')); - $promise = $index->addDocuments(self::DOCUMENTS); - $index->waitForTask($promise['taskUid']); + + $index->addDocuments(self::DOCUMENTS)->wait(); + $replacement = [ 'id' => 456, 'title' => 'The Little Prince', ]; - $promise = $index->updateDocuments([$replacement]); + $index->updateDocuments([$replacement])->wait(); - $this->assertIsValidPromise($promise); - - $index->waitForTask($promise['taskUid']); $response = $index->getDocument($replacement['id']); self::assertSame($replacement['id'], $response['id']); @@ -264,8 +247,8 @@ public function testUpdateDocuments(): void public function testUpdateDocumentsInBatches(): void { $index = $this->createEmptyIndex($this->safeIndexName('movies')); - $documentPromise = $index->addDocuments(self::DOCUMENTS); - $index->waitForTask($documentPromise['taskUid']); + + $index->addDocuments(self::DOCUMENTS)->wait(); $replacements = [ ['id' => 1, 'title' => 'Alice Outside Wonderland'], @@ -275,12 +258,11 @@ public function testUpdateDocumentsInBatches(): void ['id' => 4, 'title' => 'Harry Potter and the Half-Blood Princess'], ['id' => 456, 'title' => 'The Little Prince'], ]; - $promises = $index->updateDocumentsInBatches($replacements, 4); - self::assertCount(2, $promises); + $tasks = $index->updateDocumentsInBatches($replacements, 4); + self::assertCount(2, $tasks); - foreach ($promises as $promise) { - $this->assertIsValidPromise($promise); - $index->waitForTask($promise['taskUid']); + foreach ($tasks as $task) { + $task->wait(); } foreach ($replacements as $replacement) { @@ -298,9 +280,10 @@ public function testUpdateDocumentsByFunction(): void { $http = new Client($this->host, getenv('MEILISEARCH_API_KEY')); $http->patch('/experimental-features', ['editDocumentsByFunction' => true]); + $index = $this->createEmptyIndex($this->safeIndexName('movies')); - $documentPromise = $index->addDocuments(self::DOCUMENTS); - $index->waitForTask($documentPromise['taskUid']); + + $task = $index->addDocuments(self::DOCUMENTS)->wait(); $function = ' if doc.id % context.modulo == 0 { @@ -309,8 +292,7 @@ public function testUpdateDocumentsByFunction(): void doc.remove("comment"); doc.remove("genre"); '; - $documentPromise = $index->updateDocumentsByFunction($function, ['context' => ['modulo' => 3]]); - $index->waitForTask($documentPromise['taskUid']); + $index->updateDocumentsByFunction($function, ['context' => ['modulo' => 3]])->wait(); $documents = $index->getDocuments()->getResults(); @@ -358,13 +340,12 @@ public function testAddDocumentsCsvInBatches(): void // Total number of lines excluding header $total = \count(preg_split("/\r\n|\n|\r/", trim($documentCsv))) - 1; - $promises = $index->addDocumentsCsvInBatches($documentCsv, 250); + $tasks = $index->addDocumentsCsvInBatches($documentCsv, 250); - self::assertCount(2, $promises); + self::assertCount(2, $tasks); - foreach ($promises as $promise) { - $this->assertIsValidPromise($promise); - $index->waitForTask($promise['taskUid']); + foreach ($tasks as $task) { + $task->wait(); } $response = $index->getDocuments(); @@ -378,23 +359,25 @@ public function testAddDocumentsCsvInBatchesWithDelimiter(): void $documentCsv .= '235115704;Mister Klein'.PHP_EOL; $index = $this - ->getMockBuilder('\Meilisearch\Endpoints\Indexes') + ->getMockBuilder(Indexes::class) ->onlyMethods(['addDocumentsCsv']) ->disableOriginalConstructor() ->getMock(); $index->expects(self::exactly(2)) ->method('addDocumentsCsv') - ->willReturnCallback(function (string $documents, $primaryKey, $delimiter): void { + ->willReturnCallback(function (string $documents, $primaryKey, $delimiter): Task { static $invocation = 0; // withConsecutive has no replacement https://github.com/sebastianbergmann/phpunit/issues/4026 switch (++$invocation) { case 1: self::assertSame(["id;title\n888221515;Young folks", null, ';'], [$documents, $primaryKey, $delimiter]); - break; + + return MockTask::create(TaskType::DocumentEdition); case 2: self::assertSame(["id;title\n235115704;Mister Klein", null, ';'], [$documents, $primaryKey, $delimiter]); - break; + + return MockTask::create(TaskType::DocumentEdition); default: self::fail(); } @@ -413,13 +396,12 @@ public function testAddDocumentsNdjsonInBatches(): void $total = \count(preg_split("/\r\n|\n|\r/", trim($documentNdJson))); - $promises = $index->addDocumentsNdjsonInBatches($documentNdJson, 150); + $tasks = $index->addDocumentsNdjsonInBatches($documentNdJson, 150); - self::assertCount(2, $promises); + self::assertCount(2, $tasks); - foreach ($promises as $promise) { - $this->assertIsValidPromise($promise); - $index->waitForTask($promise['taskUid']); + foreach ($tasks as $task) { + $task->wait(); } $response = $index->getDocuments(); @@ -429,17 +411,16 @@ public function testAddDocumentsNdjsonInBatches(): void public function testAddWithUpdateDocuments(): void { $index = $this->createEmptyIndex($this->safeIndexName('movies')); - $response = $index->addDocuments(self::DOCUMENTS); - $index->waitForTask($response['taskUid']); + + $task = $index->addDocuments(self::DOCUMENTS)->wait(); + $document = [ 'id' => 9, 'title' => '1984', ]; - $promise = $index->updateDocuments([$document]); - $this->assertIsValidPromise($promise); + $task = $index->updateDocuments([$document])->wait(); - $index->waitForTask($promise['taskUid']); $response = $index->getDocument($document['id']); self::assertSame($document['id'], $response['id']); @@ -454,15 +435,13 @@ public function testAddWithUpdateDocuments(): void public function testDeleteNonExistingDocument(): void { $index = $this->createEmptyIndex($this->safeIndexName('movies')); - $response = $index->addDocuments(self::DOCUMENTS); - $index->waitForTask($response['taskUid']); + + $index->addDocuments(self::DOCUMENTS)->wait(); $documentId = 9; - $promise = $index->deleteDocument($documentId); - $this->assertIsValidPromise($promise); + $index->deleteDocument($documentId)->wait(); - $index->waitForTask($promise['taskUid']); $response = $index->getDocuments(); self::assertCount(\count(self::DOCUMENTS), $response); @@ -472,15 +451,12 @@ public function testDeleteNonExistingDocument(): void public function testDeleteSingleExistingDocumentWithDocumentIdAsInteger(): void { $index = $this->createEmptyIndex($this->safeIndexName('movies')); - $response = $index->addDocuments(self::DOCUMENTS); - $index->waitForTask($response['taskUid']); - $documentId = 123; - $promise = $index->deleteDocument($documentId); + $index->addDocuments(self::DOCUMENTS)->wait(); - $this->assertIsValidPromise($promise); + $documentId = 123; + $index->deleteDocument($documentId)->wait(); - $index->waitForTask($promise['taskUid']); $response = $index->getDocuments(); self::assertCount(\count(self::DOCUMENTS) - 1, $response); @@ -490,12 +466,11 @@ public function testDeleteSingleExistingDocumentWithDocumentIdAsInteger(): void public function testDeleteSingleExistingDocumentWithDocumentIdAsString(): void { $stringDocumentId = 'myUniqueId'; + $index = $this->createEmptyIndex($this->safeIndexName('movies')); - $addDocumentResponse = $index->addDocuments([['id' => $stringDocumentId]]); - $index->waitForTask($addDocumentResponse['taskUid']); + $index->addDocuments([['id' => $stringDocumentId]])->wait(); - $promise = $index->deleteDocument($stringDocumentId); - $index->waitForTask($promise['taskUid']); + $index->deleteDocument($stringDocumentId)->wait(); $response = $index->getDocuments(); @@ -505,14 +480,12 @@ public function testDeleteSingleExistingDocumentWithDocumentIdAsString(): void public function testDeleteMultipleDocumentsWithDocumentIdAsInteger(): void { $index = $this->createEmptyIndex($this->safeIndexName('movies')); - $response = $index->addDocuments(self::DOCUMENTS); - $index->waitForTask($response['taskUid']); - $documentIds = [1, 2]; - $promise = $index->deleteDocuments($documentIds); - $this->assertIsValidPromise($promise); + $index->addDocuments(self::DOCUMENTS)->wait(); + + $documentIds = [1, 2]; + $index->deleteDocuments($documentIds)->wait(); - $index->waitForTask($promise['taskUid']); $response = $index->getDocuments(); self::assertCount(\count(self::DOCUMENTS) - 2, $response); @@ -527,11 +500,8 @@ public function testDeleteMultipleDocumentsWithFilter(): void $index->updateFilterableAttributes(['id']); $filter = ['filter' => ['id > 0']]; - $promise = $index->deleteDocuments($filter); + $index->deleteDocuments($filter)->wait(); - $this->assertIsValidPromise($promise); - - $index->waitForTask($promise['taskUid']); $response = $index->getDocuments(); self::assertEmpty($response); @@ -567,11 +537,10 @@ public function testDeleteMultipleDocumentsWithDocumentIdAsString(): void ['id' => 'myUniqueId3'], ]; $index = $this->createEmptyIndex($this->safeIndexName('movies')); - $addDocumentResponse = $index->addDocuments($documents); - $index->waitForTask($addDocumentResponse['taskUid']); - $promise = $index->deleteDocuments(['myUniqueId1', 'myUniqueId3']); - $index->waitForTask($promise['taskUid']); + $index->addDocuments($documents)->wait(); + + $index->deleteDocuments(['myUniqueId1', 'myUniqueId3'])->wait(); $response = $index->getDocuments(); self::assertCount(1, $response); @@ -581,13 +550,11 @@ public function testDeleteMultipleDocumentsWithDocumentIdAsString(): void public function testDeleteAllDocuments(): void { $index = $this->createEmptyIndex($this->safeIndexName('movies')); - $response = $index->addDocuments(self::DOCUMENTS); - $index->waitForTask($response['taskUid']); - $promise = $index->deleteAllDocuments(); - $this->assertIsValidPromise($promise); + $index->addDocuments(self::DOCUMENTS)->wait(); + + $index->deleteAllDocuments()->wait(); - $index->waitForTask($promise['taskUid']); $response = $index->getDocuments(); self::assertCount(0, $response); @@ -612,10 +579,8 @@ public function testAddDocumentWithPrimaryKey(): void ], ]; $index = $this->createEmptyIndex($this->safeIndexName('movies-1')); - $response = $index->addDocuments($documents, 'unique'); - self::assertArrayHasKey('taskUid', $response); - $index->waitForTask($response['taskUid']); + $index->addDocuments($documents, 'unique')->wait(); self::assertSame('unique', $index->fetchPrimaryKey()); self::assertCount(1, $index->getDocuments()); @@ -631,11 +596,7 @@ public function testUpdateDocumentWithPrimaryKey(): void ], ]; $index = $this->createEmptyIndex($this->safeIndexName()); - $promise = $index->updateDocuments($documents, 'unique'); - - $this->assertIsValidPromise($promise); - - $index->waitForTask($promise['taskUid']); + $index->updateDocuments($documents, 'unique')->wait(); self::assertSame('unique', $index->fetchPrimaryKey()); self::assertCount(1, $index->getDocuments()); @@ -644,9 +605,7 @@ public function testUpdateDocumentWithPrimaryKey(): void public function testGetDocumentsWithPagination(): void { $index = $this->createEmptyIndex($this->safeIndexName('movies')); - $promise = $index->addDocuments(self::DOCUMENTS); - $this->assertIsValidPromise($promise); - $index->waitForTask($promise['taskUid']); + $index->addDocuments(self::DOCUMENTS)->wait(); $response = $index->getDocuments((new DocumentsQuery())->setLimit(3)); @@ -657,8 +616,7 @@ public function testGetDocumentsWithFilter(): void { $index = $this->createEmptyIndex($this->safeIndexName('movies')); $index->updateFilterableAttributes(['genre', 'id']); - $promise = $index->addDocuments(self::DOCUMENTS); - $index->waitForTask($promise['taskUid']); + $index->addDocuments(self::DOCUMENTS)->wait(); $response = $index->getDocuments((new DocumentsQuery())->setFilter(['id > 100'])); @@ -695,12 +653,8 @@ public function testGetDocumentsWithVector(): void { $index = $this->createEmptyIndex($this->safeIndexName('movies')); - $promise = $index->updateEmbedders(['manual' => ['source' => 'userProvided', 'dimensions' => 3]]); - $this->assertIsValidPromise($promise); - $index->waitForTask($promise['taskUid']); - $promise = $index->updateDocuments(self::VECTOR_MOVIES); - $this->assertIsValidPromise($promise); - $index->waitForTask($promise['taskUid']); + $index->updateEmbedders(['manual' => ['source' => 'userProvided', 'dimensions' => 3]])->wait(); + $index->updateDocuments(self::VECTOR_MOVIES)->wait(); $response = $index->getDocuments(new DocumentsQuery()); self::assertArrayNotHasKey('_vectors', $response->getResults()[0]); @@ -741,8 +695,7 @@ public function testUpdateDocumentsJson(): void $documentJson = fread($fileJson, filesize('./tests/datasets/small_movies.json')); fclose($fileJson); - $promise = $index->addDocumentsJson($documentJson); - $index->waitForTask($promise['taskUid']); + $index->addDocumentsJson($documentJson)->wait(); $replacement = [ [ @@ -751,8 +704,7 @@ public function testUpdateDocumentsJson(): void ], ]; - $promise = $index->updateDocumentsJson(json_encode($replacement)); - $index->waitForTask($promise['taskUid']); + $index->updateDocumentsJson(json_encode($replacement))->wait(); $response = $index->getDocument($replacement[0]['id']); @@ -772,14 +724,12 @@ public function testUpdateDocumentsCsv(): void $documentCsv = fread($fileCsv, filesize('./tests/datasets/songs.csv')); fclose($fileCsv); - $promise = $index->addDocumentsCsv($documentCsv); - $index->waitForTask($promise['taskUid']); + $index->addDocumentsCsv($documentCsv)->wait(); $replacement = 'id,title'.PHP_EOL; $replacement .= '888221515,Young folks'.PHP_EOL; - $promise = $index->updateDocumentsCsv($replacement); - $index->waitForTask($promise['taskUid']); + $index->updateDocumentsCsv($replacement)->wait(); $response = $index->getDocument(888221515); @@ -797,14 +747,12 @@ public function testUpdateDocumentsCsvWithDelimiter(): void $csv = file_get_contents('./tests/datasets/songs.csv', true); - $promise = $index->addDocumentsCsv($csv); - $index->waitForTask($promise['taskUid']); + $index->addDocumentsCsv($csv)->wait(); $replacement = 'id|title'.PHP_EOL; $replacement .= '888221515|Young folks'.PHP_EOL; - $promise = $index->updateDocumentsCsv($replacement, null, '|'); - $index->waitForTask($promise['taskUid']); + $index->updateDocumentsCsv($replacement, null, '|')->wait(); $response = $index->getDocument(888221515); @@ -820,14 +768,12 @@ public function testUpdateDocumentsNdjson(): void $documentNdJson = fread($fileNdJson, filesize('./tests/datasets/songs.ndjson')); fclose($fileNdJson); - $promise = $index->addDocumentsNdjson($documentNdJson); - $index->waitForTask($promise['taskUid']); + $index->addDocumentsNdjson($documentNdJson)->wait(); $replacement = json_encode(['id' => 412559401, 'title' => 'WASPTHOVEN']).PHP_EOL; $replacement .= json_encode(['id' => 70764404, 'artist' => 'Ailitp']).PHP_EOL; - $promise = $index->updateDocumentsNdjson($replacement); - $index->waitForTask($promise['taskUid']); + $index->updateDocumentsNdjson($replacement)->wait(); $response = $index->getDocument(412559401); self::assertSame(412559401, (int) $response['id']); @@ -848,18 +794,16 @@ public function testUpdateDocumentsCsvInBatches(): void $documentCsv = file_get_contents('./tests/datasets/songs.csv', true); - $addPromise = $index->addDocumentsCsv($documentCsv); - $index->waitForTask($addPromise['taskUid']); + $index->addDocumentsCsv($documentCsv)->wait(); $replacement = 'id,title'.PHP_EOL; $replacement .= '888221515,Young folks'.PHP_EOL; $replacement .= '235115704,Mister Klein'.PHP_EOL; - $promises = $index->updateDocumentsCsvInBatches($replacement, 1); - self::assertCount(2, $promises); - foreach ($promises as $promise) { - $this->assertIsValidPromise($promise); - $index->waitForTask($promise['taskUid']); + $tasks = $index->updateDocumentsCsvInBatches($replacement, 1); + self::assertCount(2, $tasks); + foreach ($tasks as $task) { + $task->wait(); } $response = $index->getDocument(888221515); @@ -878,23 +822,25 @@ public function testUpdateDocumentsCsvInBatchesWithDelimiter(): void $replacement .= '235115704;Mister Klein'.PHP_EOL; $index = $this - ->getMockBuilder('\Meilisearch\Endpoints\Indexes') + ->getMockBuilder(Indexes::class) ->onlyMethods(['updateDocumentsCsv']) ->disableOriginalConstructor() ->getMock(); $index->expects(self::atLeastOnce()) ->method('updateDocumentsCsv') - ->willReturnCallback(function (string $documents, $primaryKey, $delimiter): void { + ->willReturnCallback(function (string $documents, $primaryKey, $delimiter): Task { static $invocation = 0; // withConsecutive has no replacement https://github.com/sebastianbergmann/phpunit/issues/4026 switch (++$invocation) { case 1: self::assertSame(["id;title\n888221515;Young folks", null, ';'], [$documents, $primaryKey, $delimiter]); - break; + + return MockTask::create(TaskType::DocumentEdition); case 2: self::assertSame(["id;title\n235115704;Mister Klein", null, ';'], [$documents, $primaryKey, $delimiter]); - break; + + return MockTask::create(TaskType::DocumentEdition); default: self::fail(); } @@ -911,17 +857,15 @@ public function testUpdateDocumentsNdjsonInBatches(): void $documentNdJson = fread($fileNdJson, filesize('./tests/datasets/songs.ndjson')); fclose($fileNdJson); - $addPromise = $index->addDocumentsNdjson($documentNdJson); - $index->waitForTask($addPromise['taskUid']); + $index->addDocumentsNdjson($documentNdJson)->wait(); $replacement = json_encode(['id' => 412559401, 'title' => 'WASPTHOVEN']).PHP_EOL; $replacement .= json_encode(['id' => 70764404, 'artist' => 'Ailitp']).PHP_EOL; - $promises = $index->updateDocumentsNdjsonInBatches($replacement, 1); - self::assertCount(2, $promises); - foreach ($promises as $promise) { - $this->assertIsValidPromise($promise); - $index->waitForTask($promise['taskUid']); + $tasks = $index->updateDocumentsNdjsonInBatches($replacement, 1); + self::assertCount(2, $tasks); + foreach ($tasks as $task) { + $task->wait(); } $response = $index->getDocument(412559401); @@ -933,42 +877,7 @@ public function testUpdateDocumentsNdjsonInBatches(): void self::assertSame('Ailitp', $response['artist']); } - /** - * @dataProvider invalidDocumentIds - */ - public function testFetchingDocumentWithInvalidId($documentId): void - { - $index = $this->createEmptyIndex($this->safeIndexName('movies-1')); - - $this->expectException(InvalidArgumentException::class); - $index->getDocument($documentId); - } - - /** - * @dataProvider invalidDocumentIds - */ - public function testDeletingDocumentWithInvalidId($documentId): void - { - $index = $this->createEmptyIndex($this->safeIndexName('movies-1')); - - $this->expectException(InvalidArgumentException::class); - $index->deleteDocument($documentId); - } - - public static function invalidDocumentIds(): array - { - return [ - 'documentId as null' => [null], - 'documentId as bool' => [true], - 'documentId as empty string' => [''], - 'documentId as float' => [2.1], - 'documentId as array' => [[]], - 'documentId as object' => [new \stdClass()], - 'documentId as resource' => [tmpfile()], - ]; - } - - private function findDocumentWithId($documents, $documentId) + private function findDocumentWithId($documents, $documentId): ?array { foreach ($documents as $document) { if ($document['id'] === $documentId) { diff --git a/tests/Endpoints/DumpTest.php b/tests/Endpoints/DumpTest.php index ee18ff7c..33bc2d03 100644 --- a/tests/Endpoints/DumpTest.php +++ b/tests/Endpoints/DumpTest.php @@ -4,16 +4,15 @@ namespace Tests\Endpoints; +use Meilisearch\Contracts\TaskType; use Tests\TestCase; final class DumpTest extends TestCase { public function testCreateDump(): void { - $expectedKeys = ['taskUid', 'indexUid', 'status', 'type', 'enqueuedAt']; - $task = $this->client->createDump(); - self::assertSame($expectedKeys, array_keys($task)); + self::assertSame(TaskType::DumpCreation, $task->getType()); } } diff --git a/tests/Endpoints/FacetSearchTest.php b/tests/Endpoints/FacetSearchTest.php index 21fdb8e0..a2991153 100644 --- a/tests/Endpoints/FacetSearchTest.php +++ b/tests/Endpoints/FacetSearchTest.php @@ -18,8 +18,7 @@ protected function setUp(): void $this->index = $this->createEmptyIndex($this->safeIndexName()); $this->index->updateDocuments(self::DOCUMENTS); - $promise = $this->index->updateFilterableAttributes(['genre']); - $this->index->waitForTask($promise['taskUid']); + $this->index->updateFilterableAttributes(['genre'])->wait(); } public function testBasicSearchWithFilters(): void diff --git a/tests/Endpoints/IndexTest.php b/tests/Endpoints/IndexTest.php index c2ecee12..f410fc77 100644 --- a/tests/Endpoints/IndexTest.php +++ b/tests/Endpoints/IndexTest.php @@ -5,7 +5,12 @@ namespace Tests\Endpoints; use Meilisearch\Contracts\DeleteTasksQuery; +use Meilisearch\Contracts\Task; +use Meilisearch\Contracts\TaskDetails\IndexSwapDetails; +use Meilisearch\Contracts\TaskDetails\TaskDeletionDetails; use Meilisearch\Contracts\TasksQuery; +use Meilisearch\Contracts\TaskStatus; +use Meilisearch\Contracts\TaskType; use Meilisearch\Endpoints\Indexes; use Meilisearch\Exceptions\TimeOutException; use Tests\TestCase; @@ -113,9 +118,9 @@ public function testPrimaryKeyUpdate(): void { $primaryKey = 'id'; - $response = $this->index->update(['primaryKey' => $primaryKey]); - $this->client->waitForTask($response['taskUid']); - $index = $this->client->getIndex($response['indexUid']); + $task = $this->index->update(['primaryKey' => $primaryKey])->wait(); + + $index = $this->client->getIndex($task->getIndexUid()); self::assertSame($primaryKey, $index->getPrimaryKey()); self::assertSame($this->indexName, $index->getUid()); @@ -166,16 +171,13 @@ public function testGetAndFetchPrimaryKey(): void public function testGetTasks(): void { - $promise = $this->client->createIndex('new-index', ['primaryKey' => 'objectID']); - $this->index->waitForTask($promise['taskUid']); - $promise = $this->client->createIndex('other-index', ['primaryKey' => 'objectID']); - $this->index->waitForTask($promise['taskUid']); - $promise = $this->index->addDocuments([['id' => 1, 'title' => 'Pride and Prejudice']]); - $this->index->waitForTask($promise['taskUid']); + $this->client->createIndex('new-index', ['primaryKey' => 'objectID'])->wait(); + $this->client->createIndex('other-index', ['primaryKey' => 'objectID'])->wait(); + $this->index->addDocuments([['id' => 1, 'title' => 'Pride and Prejudice']])->wait(); $tasks = $this->index->getTasks((new TasksQuery())->setIndexUids(['other-index'])); - $allIndexUids = array_map(function ($val) { return $val['indexUid']; }, $tasks->getResults()); + $allIndexUids = array_map(static fn (Task $t) => $t->getIndexUid(), $tasks->getResults()); $results = array_unique($allIndexUids); $expected = [$this->index->getUid(), 'other-index']; @@ -184,56 +186,47 @@ public function testGetTasks(): void public function testWaitForTaskDefault(): void { - $promise = $this->index->addDocuments([['id' => 1, 'title' => 'Pride and Prejudice']]); - - $response = $this->index->waitForTask($promise['taskUid']); - - /* @phpstan-ignore-next-line */ - self::assertIsArray($response); - self::assertSame('succeeded', $response['status']); - self::assertSame($response['uid'], $promise['taskUid']); - self::assertArrayHasKey('type', $response); - self::assertSame('documentAdditionOrUpdate', $response['type']); - self::assertArrayHasKey('duration', $response); - self::assertArrayHasKey('startedAt', $response); - self::assertArrayHasKey('finishedAt', $response); + $task = $this->index->addDocuments([['id' => 1, 'title' => 'Pride and Prejudice']])->wait(); + + self::assertSame(TaskStatus::Succeeded, $task->getStatus()); + self::assertSame($task->getTaskUid(), $task->getTaskUid()); + self::assertSame(TaskType::DocumentAdditionOrUpdate, $task->getType()); + self::assertNotNull($task->getDuration()); + self::assertNotNull($task->getStartedAt()); + self::assertNotNull($task->getFinishedAt()); } public function testWaitForTaskWithTimeoutAndInterval(): void { - $promise = $this->index->addDocuments([['id' => 1, 'title' => 'Pride and Prejudice']]); - $response = $this->index->waitForTask($promise['taskUid'], 100, 20); - - self::assertSame('succeeded', $response['status']); - self::assertSame($response['uid'], $promise['taskUid']); - self::assertArrayHasKey('type', $response); - self::assertSame('documentAdditionOrUpdate', $response['type']); - self::assertArrayHasKey('duration', $response); - self::assertArrayHasKey('enqueuedAt', $response); - self::assertArrayHasKey('startedAt', $response); - self::assertArrayHasKey('finishedAt', $response); + $task = $this->index->addDocuments([['id' => 1, 'title' => 'Pride and Prejudice']])->wait(750, 20); + + self::assertSame(TaskStatus::Succeeded, $task->getStatus()); + self::assertSame($task->getTaskUid(), $task->getTaskUid()); + self::assertSame(TaskType::DocumentAdditionOrUpdate, $task->getType()); + self::assertNotNull($task->getDuration()); + self::assertNotNull($task->getStartedAt()); + self::assertNotNull($task->getFinishedAt()); } public function testWaitForTaskWithTimeout(): void { - $promise = $this->index->addDocuments([['id' => 1, 'title' => 'Pride and Prejudice']]); - $response = $this->index->waitForTask($promise['taskUid'], 1000); - - self::assertSame('succeeded', $response['status']); - self::assertSame($response['uid'], $promise['taskUid']); - self::assertArrayHasKey('type', $response); - self::assertSame('documentAdditionOrUpdate', $response['type']); - self::assertArrayHasKey('duration', $response); - self::assertArrayHasKey('enqueuedAt', $response); - self::assertArrayHasKey('startedAt', $response); - self::assertArrayHasKey('finishedAt', $response); + $task = $this->index->addDocuments([['id' => 1, 'title' => 'Pride and Prejudice']])->wait(1000); + + self::assertSame(TaskStatus::Succeeded, $task->getStatus()); + self::assertSame($task->getTaskUid(), $task->getTaskUid()); + self::assertSame(TaskType::DocumentAdditionOrUpdate, $task->getType()); + self::assertNotNull($task->getDuration()); + self::assertNotNull($task->getStartedAt()); + self::assertNotNull($task->getFinishedAt()); } public function testExceptionWhenTaskTimeOut(): void { - $res = $this->index->addDocuments([['id' => 1, 'title' => 'Pride and Prejudice']]); + $task = $this->index->addDocuments([['id' => 1, 'title' => 'Pride and Prejudice']]); + $this->expectException(TimeOutException::class); - $this->index->waitForTask($res['taskUid'], 0, 20); + + $task->wait(0, 20); } public function testDeleteIndexes(): void @@ -243,34 +236,29 @@ public function testDeleteIndexes(): void $indexName2 = $this->safeIndexName('books-2'); $index = $this->createEmptyIndex($indexName2); - $res = $this->index->delete(); - self::assertSame($indexName1, $res['indexUid']); - self::assertArrayHasKey('type', $res); - self::assertSame('indexDeletion', $res['type']); - self::assertArrayHasKey('enqueuedAt', $res); - - $res = $index->delete(); - self::assertSame($indexName2, $res['indexUid']); - self::assertArrayHasKey('type', $res); - self::assertSame('indexDeletion', $res['type']); - self::assertArrayHasKey('enqueuedAt', $res); + $task = $this->index->delete(); + self::assertSame($indexName1, $task->getIndexUid()); + self::assertSame(TaskType::IndexDeletion, $task->getType()); + + $task = $index->delete(); + self::assertSame($indexName2, $task->getIndexUid()); + self::assertSame(TaskType::IndexDeletion, $task->getType()); } public function testSwapIndexes(): void { - $promise = $this->client->swapIndexes([['indexA', 'indexB'], ['indexC', 'indexD']]); - $response = $this->client->waitForTask($promise['taskUid']); + $task = $this->client->swapIndexes([['indexA', 'indexB'], ['indexC', 'indexD']])->wait(); - self::assertSame([['indexes' => ['indexA', 'indexB']], ['indexes' => ['indexC', 'indexD']]], $response['details']['swaps']); + self::assertInstanceOf(IndexSwapDetails::class, $details = $task->getDetails()); + self::assertSame([['indexes' => ['indexA', 'indexB']], ['indexes' => ['indexC', 'indexD']]], $details->swaps); } public function testDeleteTasks(): void { - $promise = $this->client->deleteTasks((new DeleteTasksQuery())->setUids([1, 2])); - $response = $this->client->waitForTask($promise['taskUid']); + $task = $this->client->deleteTasks((new DeleteTasksQuery())->setUids([1, 2]))->wait(); - self::assertSame('?uids=1%2C2', $response['details']['originalFilter']); - self::assertIsNumeric($response['details']['matchedTasks']); + self::assertInstanceOf(TaskDeletionDetails::class, $details = $task->getDetails()); + self::assertSame('?uids=1%2C2', $details->originalFilter); } public function testParseDate(): void diff --git a/tests/Endpoints/MultiSearchTest.php b/tests/Endpoints/MultiSearchTest.php index 9d82ac9c..71c0f1ed 100644 --- a/tests/Endpoints/MultiSearchTest.php +++ b/tests/Endpoints/MultiSearchTest.php @@ -18,11 +18,11 @@ final class MultiSearchTest extends TestCase protected function setUp(): void { parent::setUp(); + $this->booksIndex = $this->createEmptyIndex($this->safeIndexName('books')); $this->booksIndex->updateSortableAttributes(['author']); $this->booksIndex->updateFilterableAttributes(['genre']); - $promise = $this->booksIndex->updateDocuments(self::DOCUMENTS); - $this->booksIndex->waitForTask($promise['taskUid']); + $this->booksIndex->updateDocuments(self::DOCUMENTS)->wait(); $this->songsIndex = $this->createEmptyIndex($this->safeIndexName('songs')); $this->songsIndex->updateFilterableAttributes(['duration-float']); @@ -30,8 +30,7 @@ protected function setUp(): void $documents = fread($fileCsv, filesize('./tests/datasets/songs-custom-separator.csv')); fclose($fileCsv); - $promise = $this->songsIndex->addDocumentsCsv($documents, null, '|'); - $this->songsIndex->waitForTask($promise['taskUid']); + $this->songsIndex->addDocumentsCsv($documents, null, '|')->wait(); } public function testSearchQueryData(): void diff --git a/tests/Endpoints/SearchNestedFieldsTest.php b/tests/Endpoints/SearchNestedFieldsTest.php index f98d6875..7202359c 100644 --- a/tests/Endpoints/SearchNestedFieldsTest.php +++ b/tests/Endpoints/SearchNestedFieldsTest.php @@ -14,9 +14,9 @@ final class SearchNestedFieldsTest extends TestCase protected function setUp(): void { parent::setUp(); + $this->index = $this->createEmptyIndex($this->safeIndexName('nestedIndex')); - $promise = $this->index->updateDocuments(self::NESTED_DOCUMENTS); - $this->index->waitForTask($promise['taskUid']); + $this->index->updateDocuments(self::NESTED_DOCUMENTS)->wait(); } public function testBasicSearchOnNestedFields(): void @@ -65,10 +65,9 @@ public function testSearchOnNestedFieldWithOptions(): void self::assertSame(1, $response['hits'][0]['id']); } - public function testSearchOnNestedFieldWithSearchableAtributes(): void + public function testSearchOnNestedFieldWithSearchableAttributes(): void { - $response = $this->index->updateSearchableAttributes(['title', 'info.comment']); - $this->index->waitForTask($response['taskUid']); + $this->index->updateSearchableAttributes(['title', 'info.comment'])->wait(); $response = $this->index->search('An awesome'); @@ -84,10 +83,9 @@ public function testSearchOnNestedFieldWithSearchableAtributes(): void self::assertSame(5, $response['hits'][0]['id']); } - public function testSearchOnNestedFieldWithSortableAtributes(): void + public function testSearchOnNestedFieldWithSortableAttributes(): void { - $response = $this->index->updateSortableAttributes(['info.reviewNb']); - $this->index->waitForTask($response['taskUid']); + $this->index->updateSortableAttributes(['info.reviewNb'])->wait(); $response = $this->index->search('An awesome'); @@ -105,13 +103,12 @@ public function testSearchOnNestedFieldWithSortableAtributes(): void self::assertSame(5, $response['hits'][0]['id']); } - public function testSearchOnNestedFieldWithSortableAtributesAndSearchableAttributes(): void + public function testSearchOnNestedFieldWithSortableAttributesAndSearchableAttributes(): void { - $response = $this->index->updateSettings([ + $this->index->updateSettings([ 'searchableAttributes' => ['title', 'info.comment'], 'sortableAttributes' => ['info.reviewNb'], - ]); - $this->index->waitForTask($response['taskUid']); + ])->wait(); $response = $this->index->search('An awesome'); diff --git a/tests/Endpoints/SearchTest.php b/tests/Endpoints/SearchTest.php index 258a12a6..3d7b0428 100644 --- a/tests/Endpoints/SearchTest.php +++ b/tests/Endpoints/SearchTest.php @@ -16,9 +16,9 @@ final class SearchTest extends TestCase protected function setUp(): void { parent::setUp(); + $this->index = $this->createEmptyIndex($this->safeIndexName()); - $promise = $this->index->updateDocuments(self::DOCUMENTS); - $this->index->waitForTask($promise['taskUid']); + $this->index->updateDocuments(self::DOCUMENTS)->wait(); } public function testBasicSearch(): void @@ -131,8 +131,8 @@ public function testBasicSearchIfNoPrimaryKeyAndDocumentProvided(): void public function testExceptionIfNoIndexWhenSearching(): void { $index = $this->createEmptyIndex($this->safeIndexName('movie-1')); - $res = $index->delete(); - $index->waitForTask($res['taskUid']); + + $index->delete()->wait(); $this->expectException(ApiException::class); @@ -189,8 +189,7 @@ public function testParametersWithCustomizedCropMarker(): void public function testSearchWithMatchingStrategyALL(): void { - $response = $this->index->updateSearchableAttributes(['comment']); - $this->index->waitForTask($response['taskUid']); + $this->index->updateSearchableAttributes(['comment'])->wait(); $response = $this->index->search('another french book', [ 'matchingStrategy' => 'all', @@ -201,8 +200,7 @@ public function testSearchWithMatchingStrategyALL(): void public function testSearchWithMatchingStrategyLAST(): void { - $response = $this->index->updateSearchableAttributes(['comment']); - $this->index->waitForTask($response['taskUid']); + $this->index->updateSearchableAttributes(['comment'])->wait(); $response = $this->index->search('french book', [ 'matchingStrategy' => 'last', @@ -259,8 +257,7 @@ public function testParametersWithCustomizedHighlightTag(): void public function testParametersArray(): void { - $response = $this->index->updateFilterableAttributes(['title']); - $this->index->waitForTask($response['taskUid']); + $this->index->updateFilterableAttributes(['title'])->wait(); $response = $this->index->search('prince', [ 'limit' => 5, @@ -303,8 +300,7 @@ public function testParametersArray(): void public function testParametersCanBeAStar(): void { - $response = $this->index->updateFilterableAttributes(['title']); - $this->index->waitForTask($response['taskUid']); + $this->index->updateFilterableAttributes(['title'])->wait(); $response = $this->index->search('prince', [ 'limit' => 5, @@ -347,8 +343,7 @@ public function testParametersCanBeAStar(): void public function testSearchWithFilterCanBeInt(): void { - $response = $this->index->updateFilterableAttributes(['id', 'genre']); - $this->index->waitForTask($response['taskUid']); + $this->index->updateFilterableAttributes(['id', 'genre'])->wait(); $response = $this->index->search('prince', [ 'filter' => 'id < 12', @@ -370,8 +365,7 @@ public function testSearchWithFilterCanBeInt(): void public function testBasicSearchWithFacetDistribution(): void { - $response = $this->index->updateFilterableAttributes(['genre']); - $this->index->waitForTask($response['taskUid']); + $this->index->updateFilterableAttributes(['genre'])->wait(); $response = $this->index->search('prince', [ 'facets' => ['genre'], @@ -396,8 +390,7 @@ public function testBasicSearchWithFacetDistribution(): void public function testBasicSearchWithFilters(): void { - $response = $this->index->updateFilterableAttributes(['genre']); - $this->index->waitForTask($response['taskUid']); + $this->index->updateFilterableAttributes(['genre'])->wait(); $response = $this->index->search('prince', [ 'filter' => [['genre = fantasy']], @@ -418,8 +411,7 @@ public function testBasicSearchWithFilters(): void public function testBasicSearchWithMultipleFilter(): void { - $response = $this->index->updateFilterableAttributes(['genre']); - $this->index->waitForTask($response['taskUid']); + $this->index->updateFilterableAttributes(['genre'])->wait(); $response = $this->index->search('prince', [ 'filter' => ['genre = fantasy', ['genre = fantasy', 'genre = fantasy']], @@ -440,8 +432,7 @@ public function testBasicSearchWithMultipleFilter(): void public function testCustomSearchWithFilterAndAttributesToRetrieve(): void { - $response = $this->index->updateFilterableAttributes(['genre']); - $this->index->waitForTask($response['taskUid']); + $this->index->updateFilterableAttributes(['genre'])->wait(); $response = $this->index->search('prince', [ 'filter' => [['genre = fantasy']], @@ -470,17 +461,15 @@ public function testCustomSearchWithFilterAndAttributesToRetrieve(): void public function testSearchSortWithString(): void { - $response = $this->index->updateRankingRules([ + $this->index->updateRankingRules([ 'words', 'typo', 'sort', 'proximity', 'attribute', 'exactness', - ]); - $this->index->waitForTask($response['taskUid']); - $response = $this->index->updateSortableAttributes(['genre']); - $this->index->waitForTask($response['taskUid']); + ])->wait(); + $this->index->updateSortableAttributes(['genre'])->wait(); $response = $this->index->search('prince', [ 'sort' => ['genre:asc'], @@ -501,17 +490,15 @@ public function testSearchSortWithString(): void public function testSearchSortWithInt(): void { - $response = $this->index->updateRankingRules([ + $task = $this->index->updateRankingRules([ 'words', 'typo', 'sort', 'proximity', 'attribute', 'exactness', - ]); - $this->index->waitForTask($response['taskUid']); - $response = $this->index->updateSortableAttributes(['id']); - $this->index->waitForTask($response['taskUid']); + ])->wait(); + $this->index->updateSortableAttributes(['id'])->wait(); $response = $this->index->search('prince', [ 'sort' => ['id:asc'], @@ -532,17 +519,15 @@ public function testSearchSortWithInt(): void public function testSearchSortWithMultipleParameter(): void { - $response = $this->index->updateRankingRules([ + $this->index->updateRankingRules([ 'words', 'typo', 'sort', 'proximity', 'attribute', 'exactness', - ]); - $this->index->waitForTask($response['taskUid']); - $response = $this->index->updateSortableAttributes(['id', 'title']); - $this->index->waitForTask($response['taskUid']); + ])->wait(); + $this->index->updateSortableAttributes(['id', 'title'])->wait(); $response = $this->index->search('prince', [ 'sort' => ['id:asc', 'title:asc'], @@ -667,8 +652,7 @@ function (array $hit): bool { return 'Le Petit Prince' === $hit['title']; } public function testBasicSearchWithFacetsOption(): void { - $response = $this->index->updateFilterableAttributes(['genre']); - $this->index->waitForTask($response['taskUid']); + $this->index->updateFilterableAttributes(['genre'])->wait(); $response = $this->index->search( 'prince', @@ -685,10 +669,8 @@ public function testBasicSearchWithFacetsOption(): void public function testBasicSearchWithFacetsOptionAndMultipleFacets(): void { - $response = $this->index->addDocuments([['id' => 32, 'title' => 'The Witcher', 'genre' => 'adventure', 'adaptation' => 'video game']]); - $this->index->waitForTask($response['taskUid']); - $response = $this->index->updateFilterableAttributes(['genre', 'adaptation']); - $this->index->waitForTask($response['taskUid']); + $this->index->addDocuments([['id' => 32, 'title' => 'The Witcher', 'genre' => 'adventure', 'adaptation' => 'video game']])->wait(); + $this->index->updateFilterableAttributes(['genre', 'adaptation'])->wait(); $response = $this->index->search( 'witch', @@ -709,12 +691,8 @@ public function testVectorSearch(): void { $index = $this->createEmptyIndex($this->safeIndexName()); - $promise = $index->updateEmbedders(['manual' => ['source' => 'userProvided', 'dimensions' => 3]]); - $this->assertIsValidPromise($promise); - $index->waitForTask($promise['taskUid']); - $promise = $index->updateDocuments(self::VECTOR_MOVIES); - $this->assertIsValidPromise($promise); - $index->waitForTask($promise['taskUid']); + $index->updateEmbedders(['manual' => ['source' => 'userProvided', 'dimensions' => 3]])->wait(); + $index->updateDocuments(self::VECTOR_MOVIES)->wait(); $response = $index->search('', ['vector' => [-0.5, 0.3, 0.85], 'hybrid' => ['semanticRatio' => 1.0, 'embedder' => 'manual']]); @@ -739,8 +717,7 @@ public function testShowRankingScoreDetails(): void public function testBasicSearchWithTransformFacetsDritributionOptionToFilter(): void { - $response = $this->index->updateFilterableAttributes(['genre']); - $this->index->waitForTask($response['taskUid']); + $this->index->updateFilterableAttributes(['genre'])->wait(); $filterAllFacets = function (array $facets): array { $filterOneFacet = function (array $facet): array { @@ -771,8 +748,7 @@ function (int $facetValue): bool { return 1 < $facetValue; }, public function testSearchWithAttributesToSearchOn(): void { - $response = $this->index->updateSearchableAttributes(['comment', 'title']); - $this->index->waitForTask($response['taskUid']); + $this->index->updateSearchableAttributes(['comment', 'title'])->wait(); $response = $this->index->search('the', ['attributesToSearchOn' => ['comment']]); @@ -798,10 +774,9 @@ public function testSearchWithRankingScoreThreshold(): void self::assertSame(0, $response->getHitsCount()); } - public function testBasicSearchWithTransformFacetsDritributionOptionToMap(): void + public function testBasicSearchWithTransformFacetsDistributionOptionToMap(): void { - $response = $this->index->updateFilterableAttributes(['genre']); - $this->index->waitForTask($response['taskUid']); + $this->index->updateFilterableAttributes(['genre'])->wait(); $facetsToUpperFunc = function (array $facets): array { $changeOneFacet = function (array $facet): array { @@ -831,10 +806,9 @@ public function testBasicSearchWithTransformFacetsDritributionOptionToMap(): voi self::assertSame(1, $response->getFacetDistribution()['genre']['ADVENTURE']); } - public function testBasicSearchWithTransformFacetsDritributionOptionToOder(): void + public function testBasicSearchWithTransformFacetsDistributionOptionToOrder(): void { - $response = $this->index->updateFilterableAttributes(['genre']); - $this->index->waitForTask($response['taskUid']); + $this->index->updateFilterableAttributes(['genre'])->wait(); $facetsToUpperFunc = function (array $facets): array { $sortOneFacet = function (array $facet): array { @@ -867,8 +841,7 @@ public function testSearchAndRetrieveFacetStats(): void $this->index = $this->createEmptyIndex($this->safeIndexName()); $this->index->updateFilterableAttributes(['info.reviewNb']); - $promise = $this->index->updateDocuments(self::NESTED_DOCUMENTS); - $this->index->waitForTask($promise['taskUid']); + $this->index->updateDocuments(self::NESTED_DOCUMENTS)->wait(); $response = $this->index->search( null, @@ -883,8 +856,7 @@ public function testSearchWithDistinctAttribute(): void $this->index = $this->createEmptyIndex($this->safeIndexName()); $this->index->updateFilterableAttributes(['genre']); - $promise = $this->index->updateDocuments(self::DOCUMENTS); - $this->index->waitForTask($promise['taskUid']); + $this->index->updateDocuments(self::DOCUMENTS)->wait(); $response = $this->index->search(null, [ 'distinct' => 'genre', @@ -913,8 +885,7 @@ public function testSearchWithLocales(): void { $this->index = $this->createEmptyIndex($this->safeIndexName()); $this->index->updateDocuments(self::DOCUMENTS); - $promise = $this->index->updateLocalizedAttributes([['attributePatterns' => ['title', 'comment'], 'locales' => ['fra', 'eng']]]); - $this->index->waitForTask($promise['taskUid']); + $this->index->updateLocalizedAttributes([['attributePatterns' => ['title', 'comment'], 'locales' => ['fra', 'eng']]])->wait(); $response = $this->index->search('french', [ 'locales' => ['fra', 'eng'], diff --git a/tests/Endpoints/SimilarDocumentsTest.php b/tests/Endpoints/SimilarDocumentsTest.php index c5d6e40c..d617fab9 100644 --- a/tests/Endpoints/SimilarDocumentsTest.php +++ b/tests/Endpoints/SimilarDocumentsTest.php @@ -22,8 +22,7 @@ protected function setUp(): void public function testBasicSearchWithSimilarDocuments(): void { - $task = $this->index->updateSettings(['embedders' => ['manual' => ['source' => 'userProvided', 'dimensions' => 3]]]); - $this->client->waitForTask($task['taskUid']); + $this->index->updateSettings(['embedders' => ['manual' => ['source' => 'userProvided', 'dimensions' => 3]]])->wait(); $response = $this->index->search('room'); diff --git a/tests/Endpoints/SnapshotsTest.php b/tests/Endpoints/SnapshotsTest.php index 509100af..e838491a 100644 --- a/tests/Endpoints/SnapshotsTest.php +++ b/tests/Endpoints/SnapshotsTest.php @@ -4,17 +4,15 @@ namespace Tests\Endpoints; +use Meilisearch\Contracts\TaskType; use Tests\TestCase; final class SnapshotsTest extends TestCase { public function testCreateSnapshots(): void { - $expectedKeys = ['taskUid', 'indexUid', 'status', 'type', 'enqueuedAt']; - $task = $this->client->createSnapshot(); - self::assertSame($expectedKeys, array_keys($task)); - self::assertSame('snapshotCreation', $task['type']); + self::assertSame(TaskType::SnapshotCreation, $task->getType()); } } diff --git a/tests/Endpoints/TasksTest.php b/tests/Endpoints/TasksTest.php index 49bf8fa3..c7a3f220 100644 --- a/tests/Endpoints/TasksTest.php +++ b/tests/Endpoints/TasksTest.php @@ -5,7 +5,12 @@ namespace Tests\Endpoints; use Meilisearch\Contracts\CancelTasksQuery; +use Meilisearch\Contracts\Task; +use Meilisearch\Contracts\TaskDetails\DocumentAdditionOrUpdateDetails; +use Meilisearch\Contracts\TaskDetails\TaskCancelationDetails; use Meilisearch\Contracts\TasksQuery; +use Meilisearch\Contracts\TaskStatus; +use Meilisearch\Contracts\TaskType; use Meilisearch\Endpoints\Indexes; use Meilisearch\Exceptions\ApiException; use Tests\TestCase; @@ -24,98 +29,67 @@ protected function setUp(): void public function testGetOneTaskFromWaitTask(): void { - [$promise, $response] = $this->seedIndex(); - - self::assertIsArray($response); - self::assertArrayHasKey('status', $response); - self::assertSame($response['uid'], $promise['taskUid']); - self::assertArrayHasKey('type', $response); - self::assertSame('documentAdditionOrUpdate', $response['type']); - self::assertArrayHasKey('indexUid', $response); - self::assertSame($this->indexName, $response['indexUid']); - self::assertArrayHasKey('enqueuedAt', $response); - self::assertArrayHasKey('startedAt', $response); - self::assertArrayHasKey('finishedAt', $response); - self::assertIsArray($response['details']); + [$task, $completedTask] = $this->seedIndex(); + + self::assertSame($completedTask->getTaskUid(), $task->getTaskUid()); + self::assertSame(TaskType::DocumentAdditionOrUpdate, $completedTask->getType()); + self::assertSame($this->indexName, $completedTask->getIndexUid()); + self::assertInstanceOf(DocumentAdditionOrUpdateDetails::class, $completedTask->getDetails()); } public function testGetOneTaskClient(): void { - [$promise, $response] = $this->seedIndex(); - - self::assertIsArray($promise); - $response = $this->client->getTask($promise['taskUid']); - self::assertArrayHasKey('status', $response); - self::assertSame($response['uid'], $promise['taskUid']); - self::assertArrayHasKey('type', $response); - self::assertSame('documentAdditionOrUpdate', $response['type']); - self::assertArrayHasKey('indexUid', $response); - self::assertSame($this->indexName, $response['indexUid']); - self::assertArrayHasKey('enqueuedAt', $response); - self::assertArrayHasKey('startedAt', $response); - self::assertArrayHasKey('finishedAt', $response); - self::assertIsArray($response['details']); + [$seedTask] = $this->seedIndex(); + + $task = $this->index->getTask($seedTask->getTaskUid()); + self::assertSame($task->getTaskUid(), $seedTask->getTaskUid()); + self::assertSame(TaskType::DocumentAdditionOrUpdate, $task->getType()); + self::assertSame($this->indexName, $task->getIndexUid()); + self::assertInstanceOf(DocumentAdditionOrUpdateDetails::class, $task->getDetails()); } public function testGetAllTasksClient(): void { - $response = $this->client->getTasks(); - $firstIndex = $response->getResults()[0]['uid']; + $tasks = $this->client->getTasks(); + $firstIndex = $tasks->getResults()[0]->getTaskUid(); $this->seedIndex(); - $response = $this->client->getTasks(); - $newFirstIndex = $response->getResults()[0]['uid']; + $tasks = $this->client->getTasks(); + $newFirstIndex = $tasks->getResults()[0]->getTaskUid(); self::assertNotSame($firstIndex, $newFirstIndex); } public function testGetAllTasksClientWithPagination(): void { - $response = $this->client->getTasks((new TasksQuery())->setLimit(0)); + $tasks = $this->client->getTasks((new TasksQuery())->setLimit(0)); - self::assertSame([], $response->getResults()); + self::assertSame([], $tasks->getResults()); } public function getAllTasksClientWithBatchFilter(): void { - [$promise, $response] = $this->seedIndex(); - $task = $this->client->getTask($promise['taskUid']); + [$task] = $this->seedIndex(); + $task = $this->client->getTask($task->getTaskUid()); - $response = $this->client->getTasks((new TasksQuery()) - ->setBatchUid($task['uid']) + $tasks = $this->client->getTasks( + (new TasksQuery()) + ->setBatchUid($task->getTaskUid()) ); - self::assertGreaterThan(0, $response->getTotal()); - } - - public function testGetOneTaskIndex(): void - { - [$promise, $response] = $this->seedIndex(); - - self::assertIsArray($promise); - $response = $this->index->getTask($promise['taskUid']); - self::assertArrayHasKey('status', $response); - self::assertSame($response['uid'], $promise['taskUid']); - self::assertArrayHasKey('type', $response); - self::assertSame('documentAdditionOrUpdate', $response['type']); - self::assertArrayHasKey('indexUid', $response); - self::assertSame($this->indexName, $response['indexUid']); - self::assertArrayHasKey('enqueuedAt', $response); - self::assertArrayHasKey('startedAt', $response); - self::assertArrayHasKey('finishedAt', $response); - self::assertIsArray($response['details']); + self::assertGreaterThan(0, $tasks->getTotal()); } public function testGetAllTasksByIndex(): void { $response = $this->index->getTasks(); - $firstIndex = $response->getResults()[0]['uid']; + $firstIndex = $response->getResults()[0]->getTaskUid(); $newIndex = $this->createEmptyIndex($this->safeIndexName('movie-1')); $newIndex->updateDocuments(self::DOCUMENTS); $response = $this->index->getTasks(); - $newFirstIndex = $response->getResults()[0]['uid']; + $newFirstIndex = $response->getResults()[0]->getTaskUid(); self::assertSame($firstIndex, $newFirstIndex); } @@ -125,14 +99,14 @@ public function testGetAllTasksByIndexWithFilter(): void $response = $this->index->getTasks((new TasksQuery()) ->setAfterEnqueuedAt(new \DateTime('yesterday'))->setStatuses(['succeeded'])->setLimit(2)); - $firstIndex = $response->getResults()[0]['uid']; - self::assertSame('succeeded', $response->getResults()[0]['status']); + $firstIndex = $response->getResults()[0]->getTaskUid(); + self::assertSame(TaskStatus::Succeeded, $response->getResults()[0]->getStatus()); $newIndex = $this->createEmptyIndex($this->safeIndexName('movie-1')); $newIndex->updateDocuments(self::DOCUMENTS); $response = $this->index->getTasks(); - $newFirstIndex = $response->getResults()[0]['uid']; + $newFirstIndex = $response->getResults()[0]->getTaskUid(); self::assertSame($firstIndex, $newFirstIndex); self::assertGreaterThan(0, $response->getTotal()); @@ -142,26 +116,24 @@ public function testCancelTasksWithFilter(): void { $date = new \DateTime('yesterday'); $query = http_build_query(['afterEnqueuedAt' => $date->format(\DateTime::RFC3339)]); - $promise = $this->client->cancelTasks((new CancelTasksQuery())->setAfterEnqueuedAt($date)); - - self::assertSame('taskCancelation', $promise['type']); - $response = $this->client->waitForTask($promise['taskUid']); + $task = $this->client->cancelTasks((new CancelTasksQuery())->setAfterEnqueuedAt($date))->wait(); - self::assertSame('?'.$query, $response['details']['originalFilter']); - self::assertSame('taskCancelation', $response['type']); - self::assertSame('succeeded', $response['status']); + self::assertSame(TaskStatus::Succeeded, $task->getStatus()); + self::assertInstanceOf(TaskCancelationDetails::class, $details = $task->getDetails()); + self::assertSame('?'.$query, $details->originalFilter); } public function testGetAllTasksInReverseOrder(): void { $sampleTasks = $this->client->getTasks(new TasksQuery()); - $sampleTasksUids = array_map(fn ($task) => $task['uid'], $sampleTasks->getResults()); + + $sampleTasksUids = array_map(static fn (Task $task) => $task->getTaskUid(), $sampleTasks->getResults()); $expectedTasks = $this->client->getTasks((new TasksQuery())->setUids($sampleTasksUids)); - $expectedTasksUids = array_map(fn ($task) => $task['uid'], $expectedTasks->getResults()); + $expectedTasksUids = array_map(static fn (Task $task) => $task->getTaskUid(), $expectedTasks->getResults()); $reversedTasks = $this->client->getTasks((new TasksQuery())->setUids($sampleTasksUids)->setReverse(true)); - $reversedTasksUids = array_map(fn ($task) => $task['uid'], $reversedTasks->getResults()); + $reversedTasksUids = array_map(static fn (Task $task) => $task->getTaskUid(), $reversedTasks->getResults()); self::assertSame(array_reverse($expectedTasksUids), $reversedTasksUids); } @@ -169,14 +141,18 @@ public function testGetAllTasksInReverseOrder(): void public function testExceptionIfNoTaskIdWhenGetting(): void { $this->expectException(ApiException::class); + $this->expectExceptionMessage('Task `99999999` not found.'); + $this->index->getTask(99999999); } + /** + * @return array{0: Task, 1: Task} + */ private function seedIndex(): array { - $promise = $this->index->updateDocuments(self::DOCUMENTS); - $response = $this->client->waitForTask($promise['taskUid']); + $task = $this->index->updateDocuments(self::DOCUMENTS); - return [$promise, $response]; + return [$task, $task->wait()]; } } diff --git a/tests/Endpoints/TenantTokenTest.php b/tests/Endpoints/TenantTokenTest.php index 6c8f942f..417b3920 100644 --- a/tests/Endpoints/TenantTokenTest.php +++ b/tests/Endpoints/TenantTokenTest.php @@ -42,8 +42,7 @@ protected function tearDown(): void public function testGenerateTenantTokenWithSearchRulesOnly(): void { - $promise = $this->client->index('tenantToken')->addDocuments(self::DOCUMENTS); - $this->client->waitForTask($promise['taskUid']); + $this->client->index('tenantToken')->addDocuments(self::DOCUMENTS)->wait(); $token = $this->privateClient->generateTenantToken($this->key->getUid(), ['*']); $tokenClient = new Client($this->host, $token); @@ -55,8 +54,7 @@ public function testGenerateTenantTokenWithSearchRulesOnly(): void public function testGenerateTenantTokenWithSearchRulesAsObject(): void { - $promise = $this->client->index('tenantToken')->addDocuments(self::DOCUMENTS); - $this->client->waitForTask($promise['taskUid']); + $this->client->index('tenantToken')->addDocuments(self::DOCUMENTS)->wait(); $token = $this->privateClient->generateTenantToken($this->key->getUid(), (object) ['*' => (object) []]); $tokenClient = new Client($this->host, $token); @@ -68,12 +66,10 @@ public function testGenerateTenantTokenWithSearchRulesAsObject(): void public function testGenerateTenantTokenWithFilter(): void { - $promise = $this->client->index('tenantToken')->addDocuments(self::DOCUMENTS); - $this->client->waitForTask($promise['taskUid']); - $promiseFromFilter = $this->client->index('tenantToken')->updateFilterableAttributes([ + $this->client->index('tenantToken')->addDocuments(self::DOCUMENTS)->wait(); + $this->client->index('tenantToken')->updateFilterableAttributes([ 'id', - ]); - $this->client->waitForTask($promiseFromFilter['taskUid']); + ])->wait(); $token = $this->privateClient->generateTenantToken($this->key->getUid(), (object) ['tenantToken' => (object) ['filter' => 'id > 10']]); $tokenClient = new Client($this->host, $token); diff --git a/tests/MockTask.php b/tests/MockTask.php new file mode 100644 index 00000000..603021b1 --- /dev/null +++ b/tests/MockTask.php @@ -0,0 +1,23 @@ +createEmptyIndex($this->safeIndexName()); - $promise = $index->updateDisplayedAttributes($newAttributes); - - $this->assertIsValidPromise($promise); - $index->waitForTask($promise['taskUid']); + $index->updateDisplayedAttributes($newAttributes)->wait(); $displayedAttributes = $index->getDisplayedAttributes(); @@ -40,14 +37,8 @@ public function testResetDisplayedAttributes(): void $index = $this->createEmptyIndex($this->safeIndexName()); $newAttributes = ['title']; - $promise = $index->updateDisplayedAttributes($newAttributes); - $index->waitForTask($promise['taskUid']); - - $promise = $index->resetDisplayedAttributes(); - - $this->assertIsValidPromise($promise); - - $index->waitForTask($promise['taskUid']); + $index->updateDisplayedAttributes($newAttributes); + $index->resetDisplayedAttributes()->wait(); $displayedAttributes = $index->getDisplayedAttributes(); self::assertSame(['*'], $displayedAttributes); diff --git a/tests/Settings/DistinctAttributeTest.php b/tests/Settings/DistinctAttributeTest.php index 8afed197..c7ecc016 100644 --- a/tests/Settings/DistinctAttributeTest.php +++ b/tests/Settings/DistinctAttributeTest.php @@ -14,22 +14,22 @@ final class DistinctAttributeTest extends TestCase protected function setUp(): void { parent::setUp(); + $this->index = $this->createEmptyIndex($this->safeIndexName()); } public function testGetDefaultDistinctAttribute(): void { $response = $this->index->getDistinctAttribute(); + self::assertNull($response); } public function testUpdateDistinctAttribute(): void { $distinctAttribute = 'description'; - $promise = $this->index->updateDistinctAttribute($distinctAttribute); - $this->assertIsValidPromise($promise); - $this->index->waitForTask($promise['taskUid']); + $this->index->updateDistinctAttribute($distinctAttribute)->wait(); self::assertSame($distinctAttribute, $this->index->getDistinctAttribute()); } @@ -37,13 +37,10 @@ public function testUpdateDistinctAttribute(): void public function testResetDistinctAttribute(): void { $distinctAttribute = 'description'; - $promise = $this->index->updateDistinctAttribute($distinctAttribute); - $this->index->waitForTask($promise['taskUid']); - $promise = $this->index->resetDistinctAttribute(); + $this->index->updateDistinctAttribute($distinctAttribute)->wait(); + $this->index->resetDistinctAttribute()->wait(); - $this->assertIsValidPromise($promise); - $this->index->waitForTask($promise['taskUid']); self::assertNull($this->index->getDistinctAttribute()); } } diff --git a/tests/Settings/EmbeddersTest.php b/tests/Settings/EmbeddersTest.php index dccb5415..f68eac48 100644 --- a/tests/Settings/EmbeddersTest.php +++ b/tests/Settings/EmbeddersTest.php @@ -4,6 +4,7 @@ namespace Tests\Settings; +use Meilisearch\Contracts\TaskStatus; use Meilisearch\Endpoints\Indexes; use Meilisearch\Http\Client; use Tests\TestCase; @@ -17,6 +18,7 @@ final class EmbeddersTest extends TestCase protected function setUp(): void { parent::setUp(); + $this->index = $this->createEmptyIndex($this->safeIndexName()); } @@ -31,26 +33,17 @@ public function testUpdateEmbedders(): void { $newEmbedders = ['manual' => ['source' => 'userProvided', 'dimensions' => 3, 'binaryQuantized' => true]]; - $promise = $this->index->updateEmbedders($newEmbedders); - - $this->assertIsValidPromise($promise); - $this->index->waitForTask($promise['taskUid']); + $this->index->updateEmbedders($newEmbedders)->wait(); - $embedders = $this->index->getEmbedders(); - - self::assertSame($newEmbedders, $embedders); + self::assertSame($newEmbedders, $this->index->getEmbedders()); } public function testResetEmbedders(): void { - $promise = $this->index->resetEmbedders(); - - $this->assertIsValidPromise($promise); + $this->index->updateEmbedders(['manual' => ['source' => 'userProvided', 'dimensions' => 3, 'binaryQuantized' => true]])->wait(); + $this->index->resetEmbedders()->wait(); - $this->index->waitForTask($promise['taskUid']); - $embedders = $this->index->getEmbedders(); - - self::assertSame(self::DEFAULT_EMBEDDER, $embedders); + self::assertSame(self::DEFAULT_EMBEDDER, $this->index->getEmbedders()); } public function testHuggingFacePooling(): void @@ -61,16 +54,13 @@ public function testHuggingFacePooling(): void 'pooling' => 'useModel', ]; - $promise = $this->index->updateEmbedders([ + $this->index->updateEmbedders([ 'embedder_name' => [ 'source' => 'huggingFace', 'model' => 'sentence-transformers/all-MiniLM-L6-v2', 'pooling' => 'useModel', ], - ]); - - $this->assertIsValidPromise($promise); - $this->index->waitForTask($promise['taskUid']); + ])->wait(); $embedders = $this->index->getEmbedders(); @@ -96,10 +86,10 @@ public function testCompositeEmbedder(): void ], ]; - $promise = $this->index->updateEmbedders([ + $task = $this->index->updateEmbedders([ 'embedder_name' => $embedder, ]); - $this->assertIsValidPromise($promise); + self::assertSame(TaskStatus::Enqueued, $task->getStatus()); } } diff --git a/tests/Settings/FacetSearchTest.php b/tests/Settings/FacetSearchTest.php index ae05199b..49b92482 100644 --- a/tests/Settings/FacetSearchTest.php +++ b/tests/Settings/FacetSearchTest.php @@ -21,29 +21,18 @@ public function testUpdateFacetSearch(): void { $index = $this->createEmptyIndex($this->safeIndexName()); - $promise = $index->updateFacetSearch(false); + $index->updateFacetSearch(false)->wait(); - $this->assertIsValidPromise($promise); - $index->waitForTask($promise['taskUid']); - - $facetSearch = $index->getFacetSearch(); - self::assertFalse($facetSearch); + self::assertFalse($index->getFacetSearch()); } public function testResetFacetSearch(): void { $index = $this->createEmptyIndex($this->safeIndexName()); - $promise = $index->updateFacetSearch(false); - $index->waitForTask($promise['taskUid']); - - $promise = $index->resetFacetSearch(); - - $this->assertIsValidPromise($promise); + $index->updateFacetSearch(false)->wait(); + $index->resetFacetSearch()->wait(); - $index->waitForTask($promise['taskUid']); - - $facetSearch = $index->getFacetSearch(); - self::assertTrue($facetSearch); + self::assertTrue($index->getFacetSearch()); } } diff --git a/tests/Settings/FacetingAttributesTest.php b/tests/Settings/FacetingAttributesTest.php index 9c4e0fb2..cda81b63 100644 --- a/tests/Settings/FacetingAttributesTest.php +++ b/tests/Settings/FacetingAttributesTest.php @@ -12,14 +12,12 @@ public function testGetDefaultFaceting(): void { $index = $this->createEmptyIndex($this->safeIndexName('books-1')); - $attributes = $index->getFaceting(); - self::assertSame([ 'maxValuesPerFacet' => 100, 'sortFacetValuesBy' => [ '*' => 'alpha', ], - ], $attributes); + ], $index->getFaceting()); } public function testUpdateFacetingAttributes(): void @@ -27,15 +25,12 @@ public function testUpdateFacetingAttributes(): void $newAttributes = ['sortFacetValuesBy' => ['*' => 'count']]; $index = $this->createEmptyIndex($this->safeIndexName()); - $promise = $index->updateFaceting($newAttributes); - $index->waitForTask($promise['taskUid']); - - $faceting = $index->getFaceting(); + $index->updateFaceting($newAttributes)->wait(); self::assertSame([ 'maxValuesPerFacet' => 100, 'sortFacetValuesBy' => ['*' => 'count'], - ], $faceting); + ], $index->getFaceting()); } public function testResetFaceting(): void @@ -43,16 +38,12 @@ public function testResetFaceting(): void $newAttributes = ['sortFacetValuesBy' => ['*' => 'count']]; $index = $this->createEmptyIndex($this->safeIndexName()); - $promise = $index->updateFaceting($newAttributes); - $index->waitForTask($promise['taskUid']); - - $promise = $index->resetFaceting(); - $index->waitForTask($promise['taskUid']); + $index->updateFaceting($newAttributes)->wait(); + $index->resetFaceting()->wait(); - $faceting = $index->getFaceting(); self::assertSame([ 'maxValuesPerFacet' => 100, 'sortFacetValuesBy' => ['*' => 'alpha'], - ], $faceting); + ], $index->getFaceting()); } } diff --git a/tests/Settings/FilterableAttributesTest.php b/tests/Settings/FilterableAttributesTest.php index 6378230b..30d80dcf 100644 --- a/tests/Settings/FilterableAttributesTest.php +++ b/tests/Settings/FilterableAttributesTest.php @@ -12,9 +12,7 @@ public function testGetDefaultFilterableAttributes(): void { $index = $this->createEmptyIndex($this->safeIndexName()); - $attributes = $index->getFilterableAttributes(); - - self::assertEmpty($attributes); + self::assertEmpty($index->getFilterableAttributes()); } public function testUpdateFilterableAttributes(): void @@ -22,14 +20,9 @@ public function testUpdateFilterableAttributes(): void $expectedAttributes = ['title']; $index = $this->createEmptyIndex($this->safeIndexName()); - $promise = $index->updateFilterableAttributes($expectedAttributes); - - $this->assertIsValidPromise($promise); - $index->waitForTask($promise['taskUid']); - - $filterableAttributes = $index->getFilterableAttributes(); + $index->updateFilterableAttributes($expectedAttributes)->wait(); - self::assertSame($expectedAttributes, $filterableAttributes); + self::assertSame($expectedAttributes, $index->getFilterableAttributes()); } public function testResetFilterableAttributes(): void @@ -37,17 +30,10 @@ public function testResetFilterableAttributes(): void $index = $this->createEmptyIndex($this->safeIndexName()); $newAttributes = ['title']; - $promise = $index->updateFilterableAttributes($newAttributes); - $index->waitForTask($promise['taskUid']); - - $promise = $index->resetFilterableAttributes(); - - $this->assertIsValidPromise($promise); - - $index->waitForTask($promise['taskUid']); + $index->updateFilterableAttributes($newAttributes)->wait(); + $index->resetFilterableAttributes()->wait(); - $filterableAttributes = $index->getFilterableAttributes(); - self::assertEmpty($filterableAttributes); + self::assertEmpty($index->getFilterableAttributes()); } public function testUpdateGranularFilterableAttributes(): void @@ -68,27 +54,21 @@ public function testUpdateGranularFilterableAttributes(): void ], ]; - $promise = $index->updateFilterableAttributes($expectedAttributes); - $this->assertIsValidPromise($promise); + $index->updateFilterableAttributes($expectedAttributes)->wait(); - $index->waitForTask($promise['taskUid']); - $filterableAttributes = $index->getFilterableAttributes(); - self::assertSame($expectedAttributes, $filterableAttributes); + self::assertSame($expectedAttributes, $index->getFilterableAttributes()); } public function testUpdateGeoWithGranularFilterableAttributes(): void { $index = $this->createEmptyIndex($this->safeIndexName()); - $promise = $index->updateFilterableAttributes([ + $index->updateFilterableAttributes([ [ 'attributePatterns' => ['_geo'], ], - ]); - $this->assertIsValidPromise($promise); + ])->wait(); - $index->waitForTask($promise['taskUid']); - $filterableAttributes = $index->getFilterableAttributes(); self::assertSame([ [ 'attributePatterns' => ['_geo'], @@ -100,6 +80,6 @@ public function testUpdateGeoWithGranularFilterableAttributes(): void ], ], ], - ], $filterableAttributes); + ], $index->getFilterableAttributes()); } } diff --git a/tests/Settings/LocalizedAttributesTest.php b/tests/Settings/LocalizedAttributesTest.php index e905b6a2..f5a1e9a7 100644 --- a/tests/Settings/LocalizedAttributesTest.php +++ b/tests/Settings/LocalizedAttributesTest.php @@ -25,10 +25,7 @@ public function testUpdateLocalizedAttributes(): void $newAttributes = [['attributePatterns' => ['doggo'], 'locales' => ['fra']]]; $index = $this->createEmptyIndex($this->safeIndexName()); - $promise = $index->updateLocalizedAttributes($newAttributes); - - $this->assertIsValidPromise($promise); - $index->waitForTask($promise['taskUid']); + $index->updateLocalizedAttributes($newAttributes)->wait(); $localizedAttributes = $index->getLocalizedAttributes(); @@ -40,14 +37,8 @@ public function testResetLocalizedAttributes(): void $index = $this->createEmptyIndex($this->safeIndexName()); $newAttributes = [['attributePatterns' => ['doggo'], 'locales' => ['fra']]]; - $promise = $index->updateLocalizedAttributes($newAttributes); - $index->waitForTask($promise['taskUid']); - - $promise = $index->resetLocalizedAttributes(); - - $this->assertIsValidPromise($promise); - - $index->waitForTask($promise['taskUid']); + $index->updateLocalizedAttributes($newAttributes)->wait(); + $index->resetLocalizedAttributes()->wait(); $localizedAttributes = $index->getLocalizedAttributes(); self::assertNull($localizedAttributes); diff --git a/tests/Settings/NonSeparatorTokensTest.php b/tests/Settings/NonSeparatorTokensTest.php index 5838e2d6..5a8d44cd 100644 --- a/tests/Settings/NonSeparatorTokensTest.php +++ b/tests/Settings/NonSeparatorTokensTest.php @@ -34,22 +34,16 @@ public function testUpdateNonSeparatorTokens(): void '|', ]; - $promise = $this->index->updateNonSeparatorTokens($newNonSeparatorTokens); + $this->index->updateNonSeparatorTokens($newNonSeparatorTokens)->wait(); - $this->index->waitForTask($promise['taskUid']); - - $nonSeparatorTokens = $this->index->getNonSeparatorTokens(); - - self::assertSame($newNonSeparatorTokens, $nonSeparatorTokens); + self::assertSame($newNonSeparatorTokens, $this->index->getNonSeparatorTokens()); } public function testResetNonSeparatorTokens(): void { - $promise = $this->index->resetNonSeparatorTokens(); - - $this->index->waitForTask($promise['taskUid']); - $nonSeparatorTokens = $this->index->getNonSeparatorTokens(); + $this->index->updateNonSeparatorTokens(['/'])->wait(); + $this->index->resetNonSeparatorTokens()->wait(); - self::assertSame(self::DEFAULT_NON_SEPARATOR_TOKENS, $nonSeparatorTokens); + self::assertSame(self::DEFAULT_NON_SEPARATOR_TOKENS, $this->index->getNonSeparatorTokens()); } } diff --git a/tests/Settings/PaginationTest.php b/tests/Settings/PaginationTest.php new file mode 100644 index 00000000..7e3f2471 --- /dev/null +++ b/tests/Settings/PaginationTest.php @@ -0,0 +1,46 @@ + 1000, + ]; + + protected function setUp(): void + { + parent::setUp(); + + $this->index = $this->createEmptyIndex($this->safeIndexName()); + } + + public function testGetDefaultPagination(): void + { + $response = $this->index->getPagination(); + + self::assertSame(self::DEFAULT_PAGINATION, $response); + } + + public function testUpdatePagination(): void + { + $this->index->updatePagination(['maxTotalHits' => 100])->wait(); + + self::assertSame(['maxTotalHits' => 100], $this->index->getPagination()); + } + + public function testResetPagination(): void + { + $this->index->updatePagination(['maxTotalHits' => 100])->wait(); + $this->index->resetPagination()->wait(); + + self::assertSame(self::DEFAULT_PAGINATION, $this->index->getPagination()); + } +} diff --git a/tests/Settings/PrefixSearchTest.php b/tests/Settings/PrefixSearchTest.php index 2e93bd93..e2431789 100644 --- a/tests/Settings/PrefixSearchTest.php +++ b/tests/Settings/PrefixSearchTest.php @@ -20,30 +20,18 @@ public function testGetDefaultPrefixSearch(): void public function testUpdatePrefixSearch(): void { $index = $this->createEmptyIndex($this->safeIndexName()); + $index->updatePrefixSearch('disabled')->wait(); - $promise = $index->updatePrefixSearch('disabled'); - - $this->assertIsValidPromise($promise); - $index->waitForTask($promise['taskUid']); - - $prefixSearch = $index->getPrefixSearch(); - self::assertSame('disabled', $prefixSearch); + self::assertSame('disabled', $index->getPrefixSearch()); } public function testResetPrefixSearch(): void { $index = $this->createEmptyIndex($this->safeIndexName()); - $promise = $index->updatePrefixSearch('disabled'); - $index->waitForTask($promise['taskUid']); - - $promise = $index->resetPrefixSearch(); + $index->updatePrefixSearch('disabled')->wait(); + $index->resetPrefixSearch()->wait(); - $this->assertIsValidPromise($promise); - - $index->waitForTask($promise['taskUid']); - - $prefixSearch = $index->getPrefixSearch(); - self::assertSame('indexingTime', $prefixSearch); + self::assertSame('indexingTime', $index->getPrefixSearch()); } } diff --git a/tests/Settings/ProximityPrecisionTest.php b/tests/Settings/ProximityPrecisionTest.php index 6372238b..c3fdc0e0 100644 --- a/tests/Settings/ProximityPrecisionTest.php +++ b/tests/Settings/ProximityPrecisionTest.php @@ -14,6 +14,7 @@ final class ProximityPrecisionTest extends TestCase protected function setUp(): void { parent::setUp(); + $this->index = $this->createEmptyIndex($this->safeIndexName()); } @@ -26,23 +27,15 @@ public function testGetDefaultProximityPrecision(): void public function testUpdateProximityPrecision(): void { - $promise = $this->index->updateProximityPrecision('byAttribute'); - $this->assertIsValidPromise($promise); - $this->index->waitForTask($promise['taskUid']); + $this->index->updateProximityPrecision('byAttribute')->wait(); self::assertSame('byAttribute', $this->index->getProximityPrecision()); } public function testResetProximityPrecision(): void { - $promise = $this->index->updateProximityPrecision('byAttribute'); - $this->assertIsValidPromise($promise); - $this->index->waitForTask($promise['taskUid']); - - $promise = $this->index->resetProximityPrecision(); - - $this->assertIsValidPromise($promise); - $this->index->waitForTask($promise['taskUid']); + $this->index->updateProximityPrecision('byAttribute')->wait(); + $this->index->resetProximityPrecision()->wait(); self::assertSame('byWord', $this->index->getProximityPrecision()); } diff --git a/tests/Settings/RankingRulesTest.php b/tests/Settings/RankingRulesTest.php index fc98790f..e32c8da5 100644 --- a/tests/Settings/RankingRulesTest.php +++ b/tests/Settings/RankingRulesTest.php @@ -41,10 +41,7 @@ public function testUpdateRankingRules(): void 'description:desc', ]; - $promise = $this->index->updateRankingRules($newRankingRules); - - $this->assertIsValidPromise($promise); - $this->index->waitForTask($promise['taskUid']); + $this->index->updateRankingRules($newRankingRules)->wait(); $rankingRules = $this->index->getRankingRules(); @@ -53,11 +50,9 @@ public function testUpdateRankingRules(): void public function testResetRankingRules(): void { - $promise = $this->index->resetRankingRules(); - - $this->assertIsValidPromise($promise); + $this->index->updateRankingRules(['title:asc'])->wait(); + $this->index->resetRankingRules()->wait(); - $this->index->waitForTask($promise['taskUid']); $rankingRules = $this->index->getRankingRules(); self::assertSame(self::DEFAULT_RANKING_RULES, $rankingRules); diff --git a/tests/Settings/SearchCutoffMsTest.php b/tests/Settings/SearchCutoffMsTest.php index 656e579d..128765c3 100644 --- a/tests/Settings/SearchCutoffMsTest.php +++ b/tests/Settings/SearchCutoffMsTest.php @@ -14,6 +14,7 @@ final class SearchCutoffMsTest extends TestCase protected function setUp(): void { parent::setUp(); + $this->index = $this->createEmptyIndex($this->safeIndexName()); } @@ -26,23 +27,15 @@ public function testGetDefaultSearchCutoffMs(): void public function testUpdateSearchCutoffMs(): void { - $promise = $this->index->updateSearchCutoffMs(50); - $this->assertIsValidPromise($promise); - $this->index->waitForTask($promise['taskUid']); + $this->index->updateSearchCutoffMs(50)->wait(); self::assertSame(50, $this->index->getSearchCutoffMs()); } public function testResetSearchCutoffMs(): void { - $promise = $this->index->updateSearchCutoffMs(50); - $this->assertIsValidPromise($promise); - $this->index->waitForTask($promise['taskUid']); - - $promise = $this->index->resetSearchCutoffMs(); - - $this->assertIsValidPromise($promise); - $this->index->waitForTask($promise['taskUid']); + $this->index->updateSearchCutoffMs(50)->wait(); + $this->index->resetSearchCutoffMs()->wait(); self::assertNull($this->index->getSearchCutoffMs()); } diff --git a/tests/Settings/SearchableAttributesTest.php b/tests/Settings/SearchableAttributesTest.php index 1515d232..46666ea8 100644 --- a/tests/Settings/SearchableAttributesTest.php +++ b/tests/Settings/SearchableAttributesTest.php @@ -28,26 +28,17 @@ public function testUpdateSearchableAttributes(): void 'description', ]; - $promise = $indexA->updateSearchableAttributes($searchableAttributes); + $indexA->updateSearchableAttributes($searchableAttributes)->wait(); - $this->assertIsValidPromise($promise); - - $indexA->waitForTask($promise['taskUid']); - $updatedAttributes = $indexA->getSearchableAttributes(); - - self::assertSame($searchableAttributes, $updatedAttributes); + self::assertSame($searchableAttributes, $indexA->getSearchableAttributes()); } public function testResetSearchableAttributes(): void { $index = $this->createEmptyIndex($this->safeIndexName('books-1')); - $promise = $index->resetSearchableAttributes(); - - $this->assertIsValidPromise($promise); - $index->waitForTask($promise['taskUid']); - $searchableAttributes = $index->getSearchableAttributes(); + $index->resetSearchableAttributes()->wait(); - self::assertSame(['*'], $searchableAttributes); + self::assertSame(['*'], $index->getSearchableAttributes()); } } diff --git a/tests/Settings/SeparatorTokensTest.php b/tests/Settings/SeparatorTokensTest.php index bbf1de54..b9023623 100644 --- a/tests/Settings/SeparatorTokensTest.php +++ b/tests/Settings/SeparatorTokensTest.php @@ -34,22 +34,16 @@ public function testUpdateSeparatorTokens(): void '|', ]; - $promise = $this->index->updateSeparatorTokens($newSeparatorTokens); + $this->index->updateSeparatorTokens($newSeparatorTokens)->wait(); - $this->index->waitForTask($promise['taskUid']); - - $separatorTokens = $this->index->getSeparatorTokens(); - - self::assertSame($newSeparatorTokens, $separatorTokens); + self::assertSame($newSeparatorTokens, $this->index->getSeparatorTokens()); } public function testResetSeparatorTokens(): void { - $promise = $this->index->resetSeparatorTokens(); - - $this->index->waitForTask($promise['taskUid']); - $separatorTokens = $this->index->getSeparatorTokens(); + $this->index->updateSeparatorTokens(['/'])->wait(); + $this->index->resetSeparatorTokens()->wait(); - self::assertSame(self::DEFAULT_SEPARATOR_TOKENS, $separatorTokens); + self::assertSame(self::DEFAULT_SEPARATOR_TOKENS, $this->index->getSeparatorTokens()); } } diff --git a/tests/Settings/SettingsTest.php b/tests/Settings/SettingsTest.php index 3492fc85..de887c1d 100644 --- a/tests/Settings/SettingsTest.php +++ b/tests/Settings/SettingsTest.php @@ -83,15 +83,14 @@ public function testGetDefaultSettings(): void public function testUpdateSettings(): void { $index = $this->createEmptyIndex($this->safeIndexName()); - $promise = $index->updateSettings([ + + $index->updateSettings([ 'distinctAttribute' => 'title', 'rankingRules' => ['title:asc', 'typo'], 'stopWords' => ['the'], 'facetSearch' => false, 'prefixSearch' => 'disabled', - ]); - $this->assertIsValidPromise($promise); - $index->waitForTask($promise['taskUid']); + ])->wait(); $settings = $index->getSettings(); @@ -126,22 +125,17 @@ public function testUpdateSettingsWithoutOverwritingThem(): void ]; $index = $this->createEmptyIndex($this->safeIndexName()); - $promise = $index->updateSettings([ + + $index->updateSettings([ 'distinctAttribute' => 'title', 'rankingRules' => ['title:asc', 'typo'], 'stopWords' => ['the'], 'typoTolerance' => $new_typo_tolerance, - ]); - - $this->assertIsValidPromise($promise); - $index->waitForTask($promise['taskUid']); + ])->wait(); - $promise = $index->updateSettings([ + $index->updateSettings([ 'searchableAttributes' => ['title'], - ]); - - $this->assertIsValidPromise($promise); - $index->waitForTask($promise['taskUid']); + ])->wait(); $settings = $index->getSettings(); @@ -163,18 +157,14 @@ public function testUpdateSettingsWithoutOverwritingThem(): void public function testResetSettings(): void { $index = $this->createEmptyIndex($this->safeIndexName()); - $promise = $index->updateSettings([ + + $index->updateSettings([ 'distinctAttribute' => 'title', 'rankingRules' => ['title:asc', 'typo'], 'stopWords' => ['the'], - ]); - $this->assertIsValidPromise($promise); - $index->waitForTask($promise['taskUid']); - - $promise = $index->resetSettings(); + ])->wait(); - $this->assertIsValidPromise($promise); - $index->waitForTask($promise['taskUid']); + $index->resetSettings()->wait(); $settings = $index->getSettings(); diff --git a/tests/Settings/SortableAttributesTest.php b/tests/Settings/SortableAttributesTest.php index 02698a26..30a41031 100644 --- a/tests/Settings/SortableAttributesTest.php +++ b/tests/Settings/SortableAttributesTest.php @@ -12,9 +12,7 @@ public function testGetDefaultSortableAttributes(): void { $index = $this->createEmptyIndex($this->safeIndexName()); - $attributes = $index->getSortableAttributes(); - - self::assertEmpty($attributes); + self::assertEmpty($index->getSortableAttributes()); } public function testUpdateSortableAttributes(): void @@ -22,14 +20,9 @@ public function testUpdateSortableAttributes(): void $newAttributes = ['title']; $index = $this->createEmptyIndex($this->safeIndexName()); - $promise = $index->updateSortableAttributes($newAttributes); - - $this->assertIsValidPromise($promise); - $index->waitForTask($promise['taskUid']); - - $sortableAttributes = $index->getSortableAttributes(); + $index->updateSortableAttributes($newAttributes)->wait(); - self::assertSame($newAttributes, $sortableAttributes); + self::assertSame($newAttributes, $index->getSortableAttributes()); } public function testResetSortableAttributes(): void @@ -37,16 +30,9 @@ public function testResetSortableAttributes(): void $index = $this->createEmptyIndex($this->safeIndexName()); $newAttributes = ['title']; - $promise = $index->updateSortableAttributes($newAttributes); - $index->waitForTask($promise['taskUid']); - - $promise = $index->resetSortableAttributes(); - - $this->assertIsValidPromise($promise); - - $index->waitForTask($promise['taskUid']); + $index->updateSortableAttributes($newAttributes)->wait(); + $index->resetSortableAttributes()->wait(); - $sortableAttributes = $index->getSortableAttributes(); - self::assertEmpty($sortableAttributes); + self::assertEmpty($index->getSortableAttributes()); } } diff --git a/tests/Settings/StopWordsTest.php b/tests/Settings/StopWordsTest.php index 4a7148a6..888d260b 100644 --- a/tests/Settings/StopWordsTest.php +++ b/tests/Settings/StopWordsTest.php @@ -27,28 +27,16 @@ public function testGetDefaultStopWords(): void public function testUpdateStopWords(): void { $newStopWords = ['the']; - $promise = $this->index->updateStopWords($newStopWords); + $this->index->updateStopWords($newStopWords)->wait(); - $this->assertIsValidPromise($promise); - - $this->index->waitForTask($promise['taskUid']); - $stopWords = $this->index->getStopWords(); - - self::assertSame($newStopWords, $stopWords); + self::assertSame($newStopWords, $this->index->getStopWords()); } public function testResetStopWords(): void { - $promise = $this->index->updateStopWords(['the']); - $this->index->waitForTask($promise['taskUid']); - - $promise = $this->index->resetStopWords(); - - $this->assertIsValidPromise($promise); - $this->index->waitForTask($promise['taskUid']); - - $stopWords = $this->index->getStopWords(); + $this->index->updateStopWords(['the'])->wait(); + $this->index->resetStopWords()->wait(); - self::assertEmpty($stopWords); + self::assertEmpty($this->index->getStopWords()); } } diff --git a/tests/Settings/SynonymsTest.php b/tests/Settings/SynonymsTest.php index 121dbda5..c47a2b0d 100644 --- a/tests/Settings/SynonymsTest.php +++ b/tests/Settings/SynonymsTest.php @@ -27,42 +27,26 @@ public function testUpdateSynonyms(): void $newSynonyms = [ 'hp' => ['harry potter'], ]; - $promise = $this->index->updateSynonyms($newSynonyms); - $this->assertIsValidPromise($promise); + $this->index->updateSynonyms($newSynonyms)->wait(); - $this->index->waitForTask($promise['taskUid']); - $synonyms = $this->index->getSynonyms(); - - self::assertSame($newSynonyms, $synonyms); + self::assertSame($newSynonyms, $this->index->getSynonyms()); } public function testUpdateSynonymsWithEmptyArray(): void { $newSynonyms = []; - $promise = $this->index->updateSynonyms($newSynonyms); - - $this->assertIsValidPromise($promise); - $this->index->waitForTask($promise['taskUid']); - $synonyms = $this->index->getSynonyms(); + $this->index->updateSynonyms($newSynonyms)->wait(); - self::assertSame($newSynonyms, $synonyms); + self::assertSame($newSynonyms, $this->index->getSynonyms()); } public function testResetSynonyms(): void { - $promise = $this->index->updateSynonyms([ - 'hp' => ['harry potter'], - ]); - $this->index->waitForTask($promise['taskUid']); - $promise = $this->index->resetSynonyms(); + $this->index->updateSynonyms(['hp' => ['harry potter']])->wait(); + $this->index->resetSynonyms()->wait(); - $this->assertIsValidPromise($promise); - - $this->index->waitForTask($promise['taskUid']); - $synonyms = $this->index->getSynonyms(); - - self::assertEmpty($synonyms); + self::assertEmpty($this->index->getSynonyms()); } } diff --git a/tests/Settings/TypoToleranceTest.php b/tests/Settings/TypoToleranceTest.php index 9676ece3..e15bd108 100644 --- a/tests/Settings/TypoToleranceTest.php +++ b/tests/Settings/TypoToleranceTest.php @@ -19,11 +19,13 @@ final class TypoToleranceTest extends TestCase ], 'disableOnWords' => [], 'disableOnAttributes' => [], + 'disableOnNumbers' => false, ]; protected function setUp(): void { parent::setUp(); + $this->index = $this->createEmptyIndex($this->safeIndexName()); } @@ -44,22 +46,19 @@ public function testUpdateTypoTolerance(): void ], 'disableOnWords' => [], 'disableOnAttributes' => ['title'], + 'disableOnNumbers' => true, ]; - $promise = $this->index->updateTypoTolerance($newTypoTolerance); - $this->index->waitForTask($promise['taskUid']); + $this->index->updateTypoTolerance($newTypoTolerance)->wait(); $typoTolerance = $this->index->getTypoTolerance(); - $this->assertIsValidPromise($promise); self::assertSame($newTypoTolerance, $typoTolerance); } public function testResetTypoTolerance(): void { - $promise = $this->index->resetTypoTolerance(); - $this->index->waitForTask($promise['taskUid']); + $this->index->resetTypoTolerance()->wait(); $typoTolerance = $this->index->getTypoTolerance(); - $this->assertIsValidPromise($promise); self::assertSame(self::DEFAULT_TYPO_TOLERANCE, $typoTolerance); } } diff --git a/tests/Settings/WordDictionaryTest.php b/tests/Settings/WordDictionaryTest.php index f8a4fbdc..8f1a5e46 100644 --- a/tests/Settings/WordDictionaryTest.php +++ b/tests/Settings/WordDictionaryTest.php @@ -21,9 +21,7 @@ protected function setUp(): void public function testGetDefaultWordDictionary(): void { - $response = $this->index->getDictionary(); - - self::assertSame(self::DEFAULT_WORD_DICTIONARY, $response); + self::assertSame(self::DEFAULT_WORD_DICTIONARY, $this->index->getDictionary()); } public function testUpdateWordDictionary(): void @@ -33,22 +31,15 @@ public function testUpdateWordDictionary(): void 'J. R. R.', ]; - $promise = $this->index->updateDictionary($newWordDictionary); - - $this->index->waitForTask($promise['taskUid']); + $this->index->updateDictionary($newWordDictionary)->wait(); - $wordDictionary = $this->index->getDictionary(); - - self::assertSame($newWordDictionary, $wordDictionary); + self::assertSame($newWordDictionary, $this->index->getDictionary()); } public function testResetWordDictionary(): void { - $promise = $this->index->resetDictionary(); - - $this->index->waitForTask($promise['taskUid']); - $wordDictionary = $this->index->getDictionary(); + $this->index->resetDictionary()->wait(); - self::assertSame(self::DEFAULT_WORD_DICTIONARY, $wordDictionary); + self::assertSame(self::DEFAULT_WORD_DICTIONARY, $this->index->getDictionary()); } } diff --git a/tests/TestCase.php b/tests/TestCase.php index 40659830..c73e9eb6 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -88,18 +88,10 @@ protected function setUp(): void protected function tearDown(): void { $indexes = $this->client->getIndexes((new IndexesQuery())->setLimit(100))->getResults(); - $tasks = []; foreach ($indexes as $index) { - $tasks[] = $index->delete()['taskUid']; + $index->delete()->wait(); } - - $this->client->waitForTasks($tasks); - } - - public function assertIsValidPromise(array $promise): void - { - self::assertArrayHasKey('taskUid', $promise); } public function assertFinitePagination(array $response): void @@ -138,10 +130,9 @@ public function assertEstimatedPagination(array $response): void public function createEmptyIndex($indexName, $options = []): Indexes { - $response = $this->client->createIndex($indexName, $options); - $this->client->waitForTask($response['taskUid']); + $task = $this->client->createIndex($indexName, $options)->wait(); - return $this->client->getIndex($response['indexUid']); + return $this->client->getIndex($task->getIndexUid()); } public function safeIndexName(string $indexName = 'index'): string