diff --git a/src/V2/Client.php b/src/V2/Client.php index 5b0e57d6..25de2e47 100644 --- a/src/V2/Client.php +++ b/src/V2/Client.php @@ -12,6 +12,7 @@ use Mindee\V2\Http\MindeeApiV2; use Mindee\V2\Parsing\Inference\BaseResponse; use Mindee\V2\Parsing\Job\JobResponse; +use Mindee\V2\Parsing\Search\SearchResponse; /** * Mindee Client V2. @@ -32,7 +33,7 @@ class Client */ public function __construct(?string $apiKey = null) { - $this->mindeeApi = new MindeeApiV2($apiKey ?: getenv('MINDEE_V2_API_KEY')); + $this->mindeeApi = new MindeeApiV2($apiKey ?: (getenv('MINDEE_V2_API_KEY') ?: null)); } /** @@ -159,4 +160,15 @@ public function enqueueAndGetResult( . ($pollingOptions->delaySec * $retryCounter) . " seconds" ); } + + /** + * Searches for a list of available models for the given API key. + * @param string|null $modelName Optional model name to filter by. + * @param string|null $modelType Optional model type to filter by. + * @return SearchResponse The list of models matching the criteria. + */ + public function searchModels(?string $modelName = null, ?string $modelType = null): SearchResponse + { + return $this->mindeeApi->searchModels($modelName, $modelType); + } } diff --git a/src/V2/Http/MindeeApiV2.php b/src/V2/Http/MindeeApiV2.php index 4aa728c1..67c24af5 100644 --- a/src/V2/Http/MindeeApiV2.php +++ b/src/V2/Http/MindeeApiV2.php @@ -22,6 +22,7 @@ use Mindee\V2\Parsing\Error\ErrorResponse; use Mindee\V2\Parsing\Inference\BaseResponse; use Mindee\V2\Parsing\Job\JobResponse; +use Mindee\V2\Parsing\Search\SearchResponse; use ReflectionClass; use ReflectionException; use ReflectionProperty; @@ -106,7 +107,7 @@ public function __construct(?string $apiKey) throw new MindeeException( "Missing API key for call," . " check your Client configuration.You can set this using the " - . API_KEY_ENV_NAME . ' environment variable.', + . API_V2_KEY_ENV_NAME . ' environment variable.', ErrorCode::USER_INPUT_ERROR ); } @@ -386,4 +387,44 @@ private function checkValidResponse(array $result): void throw new MindeeV2HttpUnknownException(json_encode($result, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT)); } } + + /** + * @return array> Server response. + */ + private function reqGetSearchModels(?string $modelName = null, ?string $modelType = null): array + { + $url = $this->baseUrl . "/v2/search/models"; + $params = []; + if ($modelName) { + $params['name'] = $modelName; + } + if ($modelType) { + $params['model_type'] = $modelType; + } + if (!empty($params)) { + $url .= '?' . http_build_query($params); + } + + $ch = $this->initChannel(); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_HTTPGET, true); + + $resp = [ + 'data' => curl_exec($ch), + 'code' => curl_getinfo($ch, CURLINFO_HTTP_CODE), + ]; + curl_close($ch); + return $resp; + } + + /** + * Retrieves a list of models based on criteria. + * @param string|null $modelName Optional model name to filter by. + * @param string|null $modelType Optional model type to filter by. + * @return SearchResponse The list of models matching the criteria. + */ + public function searchModels(?string $modelName = null, ?string $modelType = null): SearchResponse + { + return $this->processResponse(SearchResponse::class, $this->reqGetSearchModels($modelName, $modelType)); + } } diff --git a/src/V2/Parsing/Search/ModelWebhook.php b/src/V2/Parsing/Search/ModelWebhook.php new file mode 100644 index 00000000..3ec8a33c --- /dev/null +++ b/src/V2/Parsing/Search/ModelWebhook.php @@ -0,0 +1,43 @@ +> $rawResponse + */ + public function __construct(array $rawResponse) + { + $this->id = $rawResponse['id']; + $this->name = $rawResponse['name']; + $this->url = $rawResponse['url']; + } + + public function __toString(): string + { + return ":Name: $this->name\n" + . ":ID: $this->id\n" + . ":URL: $this->url\n"; + } +} diff --git a/src/V2/Parsing/Search/Pagination.php b/src/V2/Parsing/Search/Pagination.php new file mode 100644 index 00000000..a3adf759 --- /dev/null +++ b/src/V2/Parsing/Search/Pagination.php @@ -0,0 +1,52 @@ +> $rawResponse Raw server response array. + */ + public function __construct(array $rawResponse) + { + $this->perPage = $rawResponse['per_page']; + $this->page = $rawResponse['page']; + $this->totalItems = $rawResponse['total_items']; + $this->totalPages = $rawResponse['total_pages']; + } + + /** + * @return string String representation. + */ + public function __toString(): string + { + return ":Per Page: $this->perPage\n" + . ":Page: $this->page\n" + . ":Total Items: $this->totalItems\n" + . ":Total Pages: $this->totalPages\n"; + } +} diff --git a/src/V2/Parsing/Search/SearchModel.php b/src/V2/Parsing/Search/SearchModel.php new file mode 100644 index 00000000..5a9d88a0 --- /dev/null +++ b/src/V2/Parsing/Search/SearchModel.php @@ -0,0 +1,55 @@ + List of webhooks associated with the model. + */ + public array $webhooks; + + /** + * @param array> $rawResponse Raw server response array. + */ + public function __construct(array $rawResponse) + { + $this->id = $rawResponse['id']; + $this->name = $rawResponse['name']; + $this->modelType = $rawResponse['model_type']; + $this->webhooks = array_map( + static fn($webhook) => new ModelWebhook($webhook), + $rawResponse['webhooks'] ?? [] + ); + } + + /** + * @return string String representation. + */ + public function __toString(): string + { + return ":Name: $this->name\n" + . ":ID: $this->id\n" + . ":Model Type: $this->modelType\n" + . ":Webhooks: " . implode(', ', array_map(static fn($webhook) => $webhook->name, $this->webhooks)) . "\n"; + } +} diff --git a/src/V2/Parsing/Search/SearchModels.php b/src/V2/Parsing/Search/SearchModels.php new file mode 100644 index 00000000..16d72a80 --- /dev/null +++ b/src/V2/Parsing/Search/SearchModels.php @@ -0,0 +1,47 @@ + + */ +class SearchModels extends ArrayObject implements Stringable +{ + /** + * @param array>> $prediction Raw prediction. + */ + public function __construct(array $prediction) + { + $models = array_map(static fn($entry) => new SearchModel($entry), $prediction); + + parent::__construct($models); + } + + /** + * Default string representation. + */ + public function __toString(): string + { + if ($this->count() === 0) { + return "\n"; + } + + $lines = []; + foreach ($this as $model) { + $lines[] = "* :Name: " . $model->name; + $lines[] = " :ID: " . $model->id; + $lines[] = " :Model Type: " . $model->modelType; + $lines[] = " :Webhooks: " . count($model->webhooks); + } + + return implode("\n", $lines) . "\n"; + } +} diff --git a/src/V2/Parsing/Search/SearchResponse.php b/src/V2/Parsing/Search/SearchResponse.php new file mode 100644 index 00000000..6e0960ff --- /dev/null +++ b/src/V2/Parsing/Search/SearchResponse.php @@ -0,0 +1,50 @@ +> $rawResponse Raw server response array. + */ + public function __construct(array $rawResponse) + { + parent::__construct($rawResponse); + $this->models = new SearchModels($rawResponse['models']); + $this->pagination = new Pagination($rawResponse['pagination']); + } + + /** + * @return string String representation. + */ + public function __toString(): string + { + return implode("\n", [ + 'Models', + '######', + (string) $this->models, + 'Pagination Metadata', + '###################', + (string) $this->pagination, + '', + ]); + } +} diff --git a/tests/V2/ClientV2Test.php b/tests/V2/ClientV2Test.php index ec3cc973..8d4edbad 100644 --- a/tests/V2/ClientV2Test.php +++ b/tests/V2/ClientV2Test.php @@ -139,6 +139,7 @@ public function testInferenceLoadsLocally(): void 'Supplier name mismatch' ); } + public function testInvalidBaseUrlRaisesMindeeException(): void { $this->expectException(MindeeException::class); @@ -159,4 +160,21 @@ public function testInvalidBaseUrlRaisesMindeeException(): void } } } + + public function testInvalidApiKeyRaisesMindeeException(): void + { + $original = getenv('MINDEE_V2_API_KEY') ?: null; + putenv('MINDEE_V2_API_KEY='); + $this->expectException(MindeeException::class); + $this->expectExceptionMessage('Missing API key for call, check your Client configuration.You can set this using the MINDEE_V2_API_KEY environment variable.'); + try { + $client = new Client(); + } finally { + if (null === $original) { + putenv('MINDEE_V2_API_KEY'); + } else { + putenv('MINDEE_V2_API_KEY=' . $original); + } + } + } } diff --git a/tests/V2/Parsing/SearchResponseTest.php b/tests/V2/Parsing/SearchResponseTest.php new file mode 100644 index 00000000..bc3f6fce --- /dev/null +++ b/tests/V2/Parsing/SearchResponseTest.php @@ -0,0 +1,44 @@ +models); + foreach ($response->models as $model) { + self::assertInstanceOf(SearchModel::class, $model); + self::assertNotEmpty($model->id); + self::assertNotEmpty($model->name); + } + self::assertCount(2, $response->models[0]->webhooks); + self::assertEquals("https://failure.mindee.com", $response->models[0]->webhooks[0]->url); + + self::assertEquals(50, $response->pagination->perPage); + self::assertEquals(1, $response->pagination->page); + self::assertGreaterThanOrEqual(5, $response->pagination->totalItems); + self::assertEquals(1, $response->pagination->totalPages); + } +}