From 701dcf084d678dd220e972262dc8f13fe68ef90f Mon Sep 17 00:00:00 2001 From: Constantine Nathanson Date: Thu, 18 Jan 2024 15:55:28 +0200 Subject: [PATCH] Add support for `analyze` API --- src/Api/Admin/AdminApi.php | 12 +++++ src/Api/Admin/AnalysisTrait.php | 63 +++++++++++++++++++++++ src/Api/Admin/ApiEndPoint.php | 1 + src/Api/ApiClient.php | 11 ++-- src/Api/BaseApiClient.php | 13 ++--- src/Api/Provisioning/AccountApiClient.php | 2 +- src/Configuration/ApiConfig.php | 10 ++++ tests/Helpers/MockAdminApi.php | 7 +++ tests/Helpers/MockApiClientTrait.php | 4 ++ tests/Helpers/MockApiTrait.php | 22 +++++++- tests/Helpers/RequestAssertionsTrait.php | 8 +-- tests/Unit/Admin/AnalysisTest.php | 50 ++++++++++++++++++ 12 files changed, 185 insertions(+), 18 deletions(-) create mode 100644 src/Api/Admin/AnalysisTrait.php create mode 100644 tests/Unit/Admin/AnalysisTest.php diff --git a/src/Api/Admin/AdminApi.php b/src/Api/Admin/AdminApi.php index f17d7b0c..dff21d28 100644 --- a/src/Api/Admin/AdminApi.php +++ b/src/Api/Admin/AdminApi.php @@ -11,6 +11,7 @@ namespace Cloudinary\Api\Admin; use Cloudinary\Api\ApiClient; +use Cloudinary\Configuration\Configuration; /** * Enables Cloudinary Admin API functionality. @@ -31,12 +32,18 @@ class AdminApi use UploadMappingsTrait; use MiscTrait; use MetadataFieldsTrait; + use AnalysisTrait; /** * @var ApiClient $apiClient The API client instance. */ protected $apiClient; + /** + * @var ApiClient $apiV2Client The API v2 client instance. + */ + protected $apiV2Client; + /** * AdminApi constructor. * @@ -46,5 +53,10 @@ class AdminApi public function __construct($configuration = null) { $this->apiClient = new ApiClient($configuration); + + $apiV2Configuration = new Configuration($configuration); + $apiV2Configuration->api->apiVersion = '2'; + + $this->apiV2Client = new ApiClient($apiV2Configuration); } } diff --git a/src/Api/Admin/AnalysisTrait.php b/src/Api/Admin/AnalysisTrait.php new file mode 100644 index 00000000..6757c2bb --- /dev/null +++ b/src/Api/Admin/AnalysisTrait.php @@ -0,0 +1,63 @@ +analyzeAsync($inputType, $analysisType, $uri)->wait(); + } + + /** + * Analyzes an asset with the requested analysis type asynchronously. + * + * @param string $inputType The type of input for the asset to analyze ('uri'). + * @param string $analysisType The type of analysis to run ('google_tagging', 'captioning', 'fashion'). + * @param string $uri The URI of the asset to analyze. + * + * @return PromiseInterface + * + * @see https://cloudinary.com/documentation/media_analyzer_api_reference + */ + public function analyzeAsync($inputType, $analysisType, $uri = null) + { + $endPoint = [ApiEndPoint::ANALYSIS, 'analyze']; + + $params = ['input_type' => $inputType, 'analysis_type' => $analysisType, 'uri' => $uri]; + + return $this->apiV2Client->postJsonAsync($endPoint, $params); + } +} diff --git a/src/Api/Admin/ApiEndPoint.php b/src/Api/Admin/ApiEndPoint.php index d7fc6ed1..4a6fd399 100644 --- a/src/Api/Admin/ApiEndPoint.php +++ b/src/Api/Admin/ApiEndPoint.php @@ -27,4 +27,5 @@ class ApiEndPoint const UPLOAD_PRESETS = 'upload_presets'; const UPLOAD_MAPPINGS = 'upload_mappings'; const METADATA_FIELDS = 'metadata_fields'; + const ANALYSIS = 'analysis'; } diff --git a/src/Api/ApiClient.php b/src/Api/ApiClient.php index 35d3bb52..d7691326 100644 --- a/src/Api/ApiClient.php +++ b/src/Api/ApiClient.php @@ -52,7 +52,8 @@ public function __construct($configuration = null) $this->configuration($configuration); - $this->baseUri = "{$this->api->uploadPrefix}/" . self::apiVersion() . "/{$this->cloud->cloudName}/"; + $this->baseUri = "{$this->api->uploadPrefix}/" . self::apiVersion($this->api->apiVersion) + . "/{$this->cloud->cloudName}/"; $this->createHttpClient(); } @@ -140,7 +141,7 @@ public function postFormAsync($endPoint, $formParams) */ public function postAndSignFormAsync($endPoint, $formParams) { - if (!$this->cloud->oauthToken) { + if (! $this->cloud->oauthToken) { ApiUtils::signRequest($formParams, $this->cloud); } @@ -240,7 +241,7 @@ public function postFileAsync($endPoint, $file, $parameters, $options = []) { $unsigned = ArrayUtils::get($options, 'unsigned'); - if (!$this->cloud->oauthToken && !$unsigned) { + if (! $this->cloud->oauthToken && ! $unsigned) { ApiUtils::signRequest($parameters, $this->cloud); } @@ -414,11 +415,11 @@ protected function buildHttpClientConfig() if (isset($this->cloud->oauthToken)) { $authConfig = [ - 'headers' => ['Authorization' => 'Bearer ' . $this->cloud->oauthToken] + 'headers' => ['Authorization' => 'Bearer ' . $this->cloud->oauthToken], ]; } else { $authConfig = [ - 'auth' => [$this->cloud->apiKey, $this->cloud->apiSecret] + 'auth' => [$this->cloud->apiKey, $this->cloud->apiSecret], ]; } diff --git a/src/Api/BaseApiClient.php b/src/Api/BaseApiClient.php index 22bffdcf..ca18bf47 100644 --- a/src/Api/BaseApiClient.php +++ b/src/Api/BaseApiClient.php @@ -43,11 +43,6 @@ class BaseApiClient { use LoggerTrait; - /** - * @var string Cloudinary API version - */ - const API_VERSION = '1.1'; - /** * @var array Cloudinary API Error Classes mapping between http error codes and Cloudinary exceptions */ @@ -275,13 +270,15 @@ public function putJson($endPoint, $json) /** * Gets the API version string from the version. * + * @param string $apiVersion The API version in the form Major.minor (for example: 1.1). + * * @return string API version string * * @internal */ - public static function apiVersion() + public static function apiVersion($apiVersion = ApiConfig::DEFAULT_API_VERSION) { - return 'v' . str_replace('.', '_', self::API_VERSION); + return 'v' . str_replace('.', '_', $apiVersion); } /** @@ -315,7 +312,7 @@ protected static function finalizeEndPoint($endPoint) */ protected function callAsync($method, $endPoint, $options) { - $endPoint = self::finalizeEndPoint($endPoint); + $endPoint = self::finalizeEndPoint($endPoint); $options['headers'] = ArrayUtils::mergeNonEmpty( ArrayUtils::get($options, 'headers', []), ArrayUtils::get($options, 'extra_headers', []) diff --git a/src/Api/Provisioning/AccountApiClient.php b/src/Api/Provisioning/AccountApiClient.php index b8d157f0..db2b54f6 100644 --- a/src/Api/Provisioning/AccountApiClient.php +++ b/src/Api/Provisioning/AccountApiClient.php @@ -69,7 +69,7 @@ public function init(ProvisioningConfiguration $configuration = null) $this->baseUri = sprintf( '%s/%s/%s/%s/%s/', $this->api->uploadPrefix, - self::apiVersion(), + self::apiVersion($this->api->apiVersion), self::PROVISIONING, self::ACCOUNTS, $configuration->provisioningAccount->accountId diff --git a/src/Configuration/ApiConfig.php b/src/Configuration/ApiConfig.php index 6e73a797..95f4974a 100644 --- a/src/Configuration/ApiConfig.php +++ b/src/Configuration/ApiConfig.php @@ -14,6 +14,7 @@ * Defines the global configuration when making requests to the Cloudinary API. * * @property string $uploadPrefix Used for changing default API host. + * @property string $apiVersion Used for changing default API version. * @property int|float $timeout Describing the timeout of the request in seconds. * Use 0 to wait indefinitely (the default value is 60 seconds). * @property int|float $uploadTimeout Describing the timeout of the upload request in seconds. @@ -27,11 +28,13 @@ class ApiConfig extends BaseConfigSection const CONFIG_NAME = 'api'; const DEFAULT_UPLOAD_PREFIX = 'https://api.cloudinary.com'; + const DEFAULT_API_VERSION = '1.1'; const DEFAULT_CHUNK_SIZE = 20000000; // bytes const DEFAULT_TIMEOUT = 60; // seconds // Supported parameters const UPLOAD_PREFIX = 'upload_prefix'; // FIXME: rename it! (it is actually prefix for all API calls) + const API_VERSION = 'api_version'; const API_PROXY = 'api_proxy'; const CONNECTION_TIMEOUT = 'connection_timeout'; const TIMEOUT = 'timeout'; @@ -46,6 +49,13 @@ class ApiConfig extends BaseConfigSection */ protected $uploadPrefix; + /** + * Used for changing default API version. + * + * @var string + */ + protected $apiVersion; + /** * Optional. Specifies a proxy through which to make calls to the Cloudinary API. Format: http://hostname:port. * diff --git a/tests/Helpers/MockAdminApi.php b/tests/Helpers/MockAdminApi.php index 4ef061c5..80236c9b 100644 --- a/tests/Helpers/MockAdminApi.php +++ b/tests/Helpers/MockAdminApi.php @@ -11,6 +11,8 @@ namespace Cloudinary\Test\Helpers; use Cloudinary\Api\Admin\AdminApi; +use Cloudinary\Api\ApiClient; +use Cloudinary\Configuration\Configuration; /** * Class MockAdminApi @@ -29,5 +31,10 @@ public function __construct($configuration = null) parent::__construct($configuration); $this->apiClient = new MockApiClient($configuration); + + $apiV2Configuration = new Configuration($configuration); + $apiV2Configuration->api->apiVersion = '2'; + + $this->apiV2Client = new MockApiClient($apiV2Configuration); } } diff --git a/tests/Helpers/MockApiClientTrait.php b/tests/Helpers/MockApiClientTrait.php index 7c76363c..3bd9f462 100644 --- a/tests/Helpers/MockApiClientTrait.php +++ b/tests/Helpers/MockApiClientTrait.php @@ -98,6 +98,10 @@ static function ($options, $item) { } ); } + + /** + * @return \string[][] + */ public function getLastRequestHeaders() { return $this->mockHandler->getLastRequest()->getHeaders(); diff --git a/tests/Helpers/MockApiTrait.php b/tests/Helpers/MockApiTrait.php index cea399e4..c1dad36c 100644 --- a/tests/Helpers/MockApiTrait.php +++ b/tests/Helpers/MockApiTrait.php @@ -27,13 +27,33 @@ public function getMockHandler() return $this->getApiClient()->mockHandler; } + /** + * Returns mock handler. + * + * @return MockHandler + */ + public function getV2MockHandler() + { + return $this->getApiV2Client()->mockHandler; + } + /** * Returns a mock api client. * - * @return MockApiClient + * @return \Cloudinary\Api\ApiClient|\Cloudinary\Api\UploadApiClient|MockApiClient */ public function getApiClient() { return $this->apiClient; } + + /** + * Returns a mock api client. + * + * @return \Cloudinary\Api\ApiClient|\Cloudinary\Api\UploadApiClient|MockApiClient + */ + public function getApiV2Client() + { + return $this->apiV2Client; + } } diff --git a/tests/Helpers/RequestAssertionsTrait.php b/tests/Helpers/RequestAssertionsTrait.php index fcc8943b..73f7b11f 100644 --- a/tests/Helpers/RequestAssertionsTrait.php +++ b/tests/Helpers/RequestAssertionsTrait.php @@ -92,12 +92,14 @@ protected static function assertRequestFields(RequestInterface $request, $fields * @param string $path * @param string $message */ - protected static function assertRequestUrl(RequestInterface $request, $path, $message = '') + protected static function assertRequestUrl(RequestInterface $request, $path, $message = '', $config = null) { - $config = Configuration::instance(); + if ($config == null) { + $config = Configuration::instance(); + } self::assertEquals( - '/' . ApiClient::apiVersion() . '/' . $config->cloud->cloudName . $path, + '/' . ApiClient::apiVersion($config->api->apiVersion) . '/' . $config->cloud->cloudName . $path, $request->getUri()->getPath(), $message ); diff --git a/tests/Unit/Admin/AnalysisTest.php b/tests/Unit/Admin/AnalysisTest.php new file mode 100644 index 00000000..ccc83b5b --- /dev/null +++ b/tests/Unit/Admin/AnalysisTest.php @@ -0,0 +1,50 @@ +analyze("uri", "captioning", "https://res.cloudinary.com/demo/image/upload/dog"); + + $lastRequest = $mockAdminApi->getV2MockHandler()->getLastRequest(); + $apiV2Configuration = new Configuration(); + $apiV2Configuration->api->apiVersion = '2'; + + self::assertRequestUrl($lastRequest, '/analysis/analyze', "", $apiV2Configuration); + self::assertRequestJsonBodySubset( + $lastRequest, + [ + "input_type"=> "uri", + "analysis_type"=> "captioning", + "uri"=> "https://res.cloudinary.com/demo/image/upload/dog", + ] + ); + } +}