Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for access keys management in Account Provisioning API #392

Merged
merged 6 commits into from
Nov 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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) {
}
}
}
Loading