Skip to content

Commit

Permalink
Add support for access keys management in Account Provisioning API
Browse files Browse the repository at this point in the history
  • Loading branch information
const-cloudinary authored Nov 19, 2023
1 parent 956a3c4 commit e950063
Show file tree
Hide file tree
Showing 8 changed files with 272 additions and 11 deletions.
61 changes: 60 additions & 1 deletion src/Api/Provisioning/AccountApi.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
use Cloudinary\Api\ApiResponse;
use Cloudinary\Api\ApiUtils;
use Cloudinary\Api\Exception\ApiError;
use Cloudinary\ArrayUtils;
use Cloudinary\Configuration\Provisioning\ProvisioningConfiguration;

/**
Expand All @@ -27,7 +28,7 @@ class AccountApi
/**
* @var AccountApiClient $accountApiClient
*/
private $accountApiClient;
protected $accountApiClient;

/**
* AccountApi constructor.
Expand Down Expand Up @@ -410,4 +411,62 @@ public function userGroupUsers($groupId)

return $this->accountApiClient->get($uri);
}

/**
* Gets sub account access keys.
*
* @param string $subAccountId The id of the sub account.
* @param array $options Additional options.
*
* @return ApiResponse A list of access keys.
*
* @api
*/
public function accessKeys($subAccountId, $options = [])
{
$uri = [AccountEndPoint::SUB_ACCOUNTS, $subAccountId, AccountEndPoint::ACCESS_KEYS];

$params = ArrayUtils::whitelist($options, ['page_size', 'page', 'sort_by', 'sort_order']);

return $this->accountApiClient->get($uri, $params);
}

/**
* Generates a new access key.
*
* @param string $subAccountId The id of the sub account.
* @param array $options Additional options.
*
* @return ApiResponse Generated access key.
*
* @api
*/
public function generateAccessKey($subAccountId, $options = [])
{
$uri = [AccountEndPoint::SUB_ACCOUNTS, $subAccountId, AccountEndPoint::ACCESS_KEYS];

$params = ArrayUtils::whitelist($options, ['name', 'enabled']);

return $this->accountApiClient->postJson($uri, $params);
}

/**
* Updates the access key.
*
* @param string $subAccountId The id of the sub account.
* @param string $apiKey The Api Key.
* @param array $options Additional options.
*
* @return ApiResponse Updated access key.
*
* @api
*/
public function updateAccessKey($subAccountId, $apiKey, $options = [])
{
$uri = [AccountEndPoint::SUB_ACCOUNTS, $subAccountId, AccountEndPoint::ACCESS_KEYS, $apiKey];

$params = ArrayUtils::whitelist($options, ['name', 'enabled']);

return $this->accountApiClient->putJson($uri, $params);
}
}
38 changes: 30 additions & 8 deletions src/Api/Provisioning/AccountApiClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
namespace Cloudinary\Api\Provisioning;

use Cloudinary\Api\BaseApiClient;
use Cloudinary\Configuration\ApiConfig;
use Cloudinary\Configuration\Provisioning\ProvisioningAccountConfig;
use Cloudinary\Configuration\Provisioning\ProvisioningConfiguration;
use Cloudinary\Exception\ConfigurationException;
use GuzzleHttp\Client;
Expand All @@ -27,6 +29,11 @@ class AccountApiClient extends BaseApiClient
const PROVISIONING = 'provisioning';
const ACCOUNTS = 'accounts';

/**
* @var ProvisioningAccountConfig $provisioningAccount The Account API configuration.
*/
protected $provisioningAccount;

/**
* AccountApiClient constructor
*
Expand All @@ -48,14 +55,16 @@ public function init(ProvisioningConfiguration $configuration = null)

if (empty($configuration->provisioningAccount->accountId)
|| empty($configuration->provisioningAccount->provisioningApiKey)
|| empty($configuration->provisioningAccount->provisioningApiSecret)) {
|| empty($configuration->provisioningAccount->provisioningApiSecret)
) {
throw new ConfigurationException(
'When providing account id, key or secret, all must be provided'
);
}

$this->api = $configuration->api;
$this->logging = $configuration->logging;
$this->api = $configuration->api;
$this->provisioningAccount = $configuration->provisioningAccount;
$this->logging = $configuration->logging;

$this->baseUri = sprintf(
'%s/%s/%s/%s/%s/',
Expand All @@ -66,10 +75,23 @@ public function init(ProvisioningConfiguration $configuration = null)
$configuration->provisioningAccount->accountId
);

$clientConfig = [
$this->createHttpClient();
}

protected function createHttpClient()
{
$this->httpClient = new Client($this->buildHttpClientConfig());
}

/**
* @return array
*/
protected function buildHttpClientConfig()
{
return [
'auth' => [
$configuration->provisioningAccount->provisioningApiKey,
$configuration->provisioningAccount->provisioningApiSecret,
$this->provisioningAccount->provisioningApiKey,
$this->provisioningAccount->provisioningApiSecret,
],
'base_uri' => $this->baseUri,
'connect_timeout' => $this->api->connectionTimeout,
Expand All @@ -78,7 +100,7 @@ public function init(ProvisioningConfiguration $configuration = null)
'headers' => ['User-Agent' => self::userAgent()],
'http_errors' => false, // We handle HTTP errors by ourselves and throw corresponding exceptions
];

$this->httpClient = new Client($clientConfig);
}


}
1 change: 1 addition & 0 deletions src/Api/Provisioning/AccountEndPoint.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,5 @@ class AccountEndPoint
const USERS = 'users';
const USER_GROUPS = 'user_groups';
const SUB_ACCOUNTS = 'sub_accounts';
const ACCESS_KEYS = 'access_keys';
}
43 changes: 43 additions & 0 deletions tests/Helpers/MockAccountApi.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php
/**
* This file is part of the Cloudinary PHP package.
*
* (c) Cloudinary
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Cloudinary\Test\Helpers;

use Cloudinary\Api\Provisioning\AccountApi;

/**
* Class MockAccountApi
*/
class MockAccountApi extends AccountApi
{
use MockApiTrait;

/**
* MockSearchApi constructor.
*
* @param mixed $configuration
*/
public function __construct($configuration = null)
{
parent::__construct($configuration);

$this->accountApiClient = new MockAccountApiClient($configuration);
}

/**
* Returns a mock api client.
*
* @return MockAccountApiClient
*/
public function getApiClient()
{
return $this->accountApiClient;
}
}
21 changes: 21 additions & 0 deletions tests/Helpers/MockAccountApiClient.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php
/**
* This file is part of the Cloudinary PHP package.
*
* (c) Cloudinary
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Cloudinary\Test\Helpers;

use Cloudinary\Api\Provisioning\AccountApiClient;

/**
* Class MockApiClient
*/
class MockAccountApiClient extends AccountApiClient
{
use MockApiClientTrait;
}
21 changes: 21 additions & 0 deletions tests/Helpers/RequestAssertionsTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@

use Cloudinary\Api\ApiClient;
use Cloudinary\Configuration\Configuration;
use Cloudinary\Configuration\Provisioning\ProvisioningAccountConfig;
use Cloudinary\Configuration\Provisioning\ProvisioningConfiguration;
use Psr\Http\Message\RequestInterface;

use GuzzleHttp\Psr7;
Expand Down Expand Up @@ -101,6 +103,25 @@ protected static function assertRequestUrl(RequestInterface $request, $path, $me
);
}

/**
* Assert that a request was made to the correct url.
*
* @param RequestInterface $request
* @param string $path
* @param string $message
*/
protected static function assertAccountRequestUrl(RequestInterface $request, $path, $message = '')
{
$config = ProvisioningConfiguration::instance();

self::assertEquals(
'/' . ApiClient::apiVersion() . '/' . 'provisioning/accounts/' . $config->provisioningAccount->accountId
. $path,
$request->getUri()->getPath(),
$message
);
}

/**
* Asserts that a request's query string contains the expected fields and values.
*
Expand Down
85 changes: 85 additions & 0 deletions tests/Unit/Provisioning/AccessKeysTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<?php

namespace Cloudinary\Test\Unit\Provisioning;

use Cloudinary\Test\Helpers\MockAccountApi;
use Cloudinary\Test\Helpers\RequestAssertionsTrait;

/**
* Class AccessKeysTest
*/
class AccessKeysTest extends ProvisioningUnitTestCase
{
use RequestAssertionsTrait;

const SUB_ACCOUNT_ID = 'sub_account123';
const API_KEY = 'key';

/**
* Should allow listing access keys.
*/
public function testAccessKeys()
{
$mockAccApi = new MockAccountApi();

$mockAccApi->accessKeys(
self::SUB_ACCOUNT_ID,
['page_size' => 2, 'page' => 1, 'sort_by' => 'name', 'sort_order' => 'asc']
);

$lastRequest = $mockAccApi->getMockHandler()->getLastRequest();

self::assertAccountRequestUrl($lastRequest, '/sub_accounts/' . self::SUB_ACCOUNT_ID . '/access_keys');

self::assertRequestGet($lastRequest);

self::assertRequestQueryStringSubset($lastRequest, ['page_size' => '2']);
self::assertRequestQueryStringSubset($lastRequest, ['page' => '1']);
self::assertRequestQueryStringSubset($lastRequest, ['sort_by' => 'name']);
self::assertRequestQueryStringSubset($lastRequest, ['sort_order' => 'asc']);
}

/**
* Should allow generating access keys.
*/
public function testGenerateAccessKey()
{
$mockAccApi = new MockAccountApi();

$mockAccApi->generateAccessKey(
self::SUB_ACCOUNT_ID,
['enabled' => true, 'name' => 'test_key']
);

$lastRequest = $mockAccApi->getMockHandler()->getLastRequest();

self::assertAccountRequestUrl($lastRequest, '/sub_accounts/' . self::SUB_ACCOUNT_ID . '/access_keys');
self::assertRequestPost($lastRequest);

self::assertRequestJsonBodySubset($lastRequest, ['enabled' => true, 'name' => 'test_key']);
}

/**
* Should allow updating access keys.
*/
public function testUpdateAccessKey()
{
$mockAccApi = new MockAccountApi();

$mockAccApi->updateAccessKey(
self::SUB_ACCOUNT_ID,
self::API_KEY,
['enabled' => false, 'name' => 'updated_key']
);

$lastRequest = $mockAccApi->getMockHandler()->getLastRequest();

self::assertAccountRequestUrl(
$lastRequest,
'/sub_accounts/' . self::SUB_ACCOUNT_ID . '/access_keys/' . self::API_KEY
);
self::assertRequestPut($lastRequest);

self::assertRequestJsonBodySubset($lastRequest, ['enabled' => false, 'name' => 'updated_key']);
}
}
13 changes: 11 additions & 2 deletions tests/Unit/Provisioning/ProvisioningUnitTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@
namespace Cloudinary\Test\Unit\Provisioning;

use Cloudinary\Configuration\Provisioning\ProvisioningConfiguration;
use Cloudinary\Exception\ConfigurationException;
use Cloudinary\Test\CloudinaryTestCase;

/**
* Class ProvisioningUnitTestCase
*/
abstract class ProvisioningUnitTestCase extends CloudinaryTestCase
{

const ACCOUNT_ID = 'account123';
const ACCOUNT_API_KEY = 'accountKey';
const ACCOUNT_API_SECRET = 'accountSecret';
Expand All @@ -37,12 +37,21 @@ public function setUp()
. $this::ACCOUNT_ID;

putenv(ProvisioningConfiguration::CLOUDINARY_ACCOUNT_URL_ENV_VAR . '=' . $this->accountUrl);

ProvisioningConfiguration::instance()->init();
}

public function tearDown()
{
parent::tearDown();

putenv(ProvisioningConfiguration::CLOUDINARY_ACCOUNT_URL_ENV_VAR . '=' . $this->accountUrlEnvBackup);
putenv(
ProvisioningConfiguration::CLOUDINARY_ACCOUNT_URL_ENV_VAR .
(! empty($this->accountUrlEnvBackup) ? '=' . $this->accountUrlEnvBackup : "")
);
try {
ProvisioningConfiguration::instance()->init();
} catch (ConfigurationException $ce) {
}
}
}

0 comments on commit e950063

Please sign in to comment.