diff --git a/AdobeIms/Model/FlushUserTokens.php b/AdobeIms/Model/FlushUserTokens.php
new file mode 100644
index 000000000000..21f74cdb61a8
--- /dev/null
+++ b/AdobeIms/Model/FlushUserTokens.php
@@ -0,0 +1,60 @@
+userContext = $userContext;
+ $this->userProfileRepository = $userProfileRepository;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function execute(int $adminUserId = null): void
+ {
+ try {
+ if ($adminUserId === null) {
+ $adminUserId = (int) $this->userContext->getUserId();
+ }
+
+ $userProfile = $this->userProfileRepository->getByUserId($adminUserId);
+ $userProfile->setAccessToken('');
+ $userProfile->setRefreshToken('');
+ $this->userProfileRepository->save($userProfile);
+ } catch (\Exception $e) {
+ // User profile and tokens are not present in the system
+ }
+ }
+}
diff --git a/AdobeIms/Model/GetAccessToken.php b/AdobeIms/Model/GetAccessToken.php
new file mode 100644
index 000000000000..271535dc9603
--- /dev/null
+++ b/AdobeIms/Model/GetAccessToken.php
@@ -0,0 +1,57 @@
+userContext = $userContext;
+ $this->userProfileRepository = $userProfileRepository;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function execute(int $adminUserId = null): ?string
+ {
+ try {
+ if ($adminUserId === null) {
+ $adminUserId = (int) $this->userContext->getUserId();
+ }
+ return $this->userProfileRepository->getByUserId($adminUserId)->getAccessToken();
+ } catch (NoSuchEntityException $exception) {
+ return null;
+ }
+ }
+}
diff --git a/AdobeIms/Model/LogOut.php b/AdobeIms/Model/LogOut.php
index 486ce5c83163..35411cb32c56 100644
--- a/AdobeIms/Model/LogOut.php
+++ b/AdobeIms/Model/LogOut.php
@@ -8,6 +8,7 @@
use Magento\AdobeImsApi\Api\LogOutInterface;
use Magento\AdobeImsApi\Api\Data\ConfigInterface;
use Magento\Authorization\Model\UserContextInterface;
+use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Framework\HTTP\Client\CurlFactory;
use Psr\Log\LoggerInterface;
@@ -75,12 +76,22 @@ public function __construct(
public function execute() : bool
{
try {
- $userProfile = $this->userProfileRepository->getByUserId((int)$this->userContext->getUserId());
+ try {
+ $userProfile = $this->userProfileRepository->getByUserId((int)$this->userContext->getUserId());
+ } catch (NoSuchEntityException $exception) {
+ return true;
+ }
+
+ $accessToken = $userProfile->getAccessToken();
+
+ if (empty($accessToken)) {
+ return true;
+ }
$curl = $this->curlFactory->create();
$curl->addHeader('Content-Type', 'application/x-www-form-urlencoded');
$curl->addHeader('cache-control', 'no-cache');
- $curl->get($this->config->getLogoutUrl($userProfile->getAccessToken()));
+ $curl->get($this->config->getLogoutUrl($accessToken));
if ($curl->getStatus() === self::HTTP_FOUND) {
$userProfile->setAccessToken('');
diff --git a/AdobeIms/etc/di.xml b/AdobeIms/etc/di.xml
index da69fa18f1b7..5639601d57c5 100644
--- a/AdobeIms/etc/di.xml
+++ b/AdobeIms/etc/di.xml
@@ -14,4 +14,6 @@
+
+
diff --git a/AdobeImsApi/Api/FlushUserTokensInterface.php b/AdobeImsApi/Api/FlushUserTokensInterface.php
new file mode 100644
index 000000000000..778f44e33d7d
--- /dev/null
+++ b/AdobeImsApi/Api/FlushUserTokensInterface.php
@@ -0,0 +1,24 @@
+clientConfig = $clientConfig;
- $this->imsConfig = $imsConfig;
+ $this->config = $config;
+ $this->connection = $connection;
$this->searchResultFactory = $searchResultFactory;
$this->searchParametersProvider = $searchParametersProvider;
$this->localeResolver = $localeResolver;
- $this->connectionFactory = $connectionFactory;
$this->licenseRequestFactory = $licenseRequestFactory;
$this->logger = $logger;
- $this->userProfileRepository = $userProfileRepository;
- $this->userContext = $userContext;
$this->userQuotaFactory = $userQuotaFactory;
$this->stockFileToDocument = $stockFileToDocument;
$this->licenseConfirmationFactory = $licenseConfirmationFactory;
@@ -164,20 +137,14 @@ public function search(SearchCriteriaInterface $searchCriteria): SearchResultInt
$connection = $this->getConnection();
try {
- $connection->searchFilesInitialize(
- $this->getSearchRequest($searchCriteria),
- $this->getAccessToken()
- );
+ $connection->searchFilesInitialize($this->getSearchRequest($searchCriteria));
$response = $connection->getNextResponse();
/** @var StockFile $file */
foreach ($response->getFiles() as $file) {
$items[] = $this->stockFileToDocument->convert($file);
}
$totalCount = $response->getNbResults();
- } catch (Exception $exception) {
- if (strpos($exception->getMessage(), 'Api Key is invalid') !== false) {
- throw new AuthenticationException(__($exception->getMessage()), $exception, $exception->getCode());
- }
+ } catch (IntegrationException $exception) {
$this->logger->critical($exception->getMessage());
}
@@ -201,7 +168,7 @@ private function getLicenseRequest(int $contentId): LicenseRequest
/** @var LicenseRequest $licenseRequest */
$licenseRequest = $this->licenseRequestFactory->create();
$licenseRequest->setContentId($contentId)
- ->setLocale($this->clientConfig->getLocale())
+ ->setLocale($this->config->getLocale())
->setLicenseState('STANDARD');
return $licenseRequest;
@@ -217,7 +184,7 @@ private function getLicenseRequest(int $contentId): LicenseRequest
*/
private function getLicenseInfo(int $contentId): License
{
- return $this->getConnection()->getMemberProfile($this->getLicenseRequest($contentId), $this->getAccessToken());
+ return $this->getConnection()->getMemberProfile($this->getLicenseRequest($contentId));
}
/**
@@ -250,31 +217,19 @@ public function getLicenseConfirmation(int $contentId): LicenseConfirmationInter
}
/**
- * Performs image license request to Adobe Stock APi
- *
- * @param int $contentId
- * @throws IntegrationException
- * @throws StockApi
+ * @inheritdoc
*/
public function licenseImage(int $contentId): void
{
- $licenseRequest = $this->getLicenseRequest($contentId);
- $this->getConnection()->getContentLicense($licenseRequest, $this->getAccessToken());
+ $this->getConnection()->getContentLicense($this->getLicenseRequest($contentId));
}
/**
- * Returns download URL for a licensed image
- *
- * @param int $contentId
- * @return string
- * @throws IntegrationException
- * @throws \AdobeStock\Api\Exception\StockApi
+ * @inheritdoc
*/
public function getImageDownloadUrl(int $contentId): string
{
- $licenseRequest = $this->getLicenseRequest($contentId);
-
- return $this->getConnection()->downloadAssetUrl($licenseRequest, $this->getAccessToken());
+ return $this->getConnection()->downloadAssetUrl($this->getLicenseRequest($contentId));
}
/**
@@ -305,7 +260,7 @@ private function getResultColumns(): array
{
$resultsColumns = Constants::getResultColumns();
$resultColumnArray = [];
- foreach ($this->clientConfig->getSearchResultFields() as $field) {
+ foreach ($this->config->getSearchResultFields() as $field) {
if (!isset($resultsColumns[$field])) {
$message = __('Cannot retrieve the field %1. It\'s not available in Adobe Stock SDK', $field);
$this->logger->critical($message);
@@ -318,90 +273,17 @@ private function getResultColumns(): array
/**
* Initialize connection to the Adobe Stock service.
- *
- * @param string $key
- *
- * @return AdobeStock
- * @throws IntegrationException
- */
- private function getConnection(string $key = null): AdobeStock
- {
- try {
- $apiKey = !empty($key) ? $key : (string) $this->imsConfig->getApiKey();
- return $this->connectionFactory->create(
- $apiKey,
- (string) $this->clientConfig->getProductName(),
- (string) $this->clientConfig->getTargetEnvironment()
- );
- } catch (Exception $exception) {
- $message = __(
- 'An error occurred during Adobe Stock connection initialization: %error_message',
- ['error_message' => $exception->getMessage()]
- );
- $this->processException($message, $exception);
- }
- }
-
- /**
- * Retrieve an access token for current user
- *
- * @return string|null
- */
- private function getAccessToken()
- {
- try {
- return $this->userProfileRepository->getByUserId(
- (int)$this->userContext->getUserId()
- )->getAccessToken();
- } catch (NoSuchEntityException $exception) {
- return null;
- }
- }
-
- /**
- * Test connection to Adobe Stock API
- *
- * @param string $apiKey
- *
- * @return bool
*/
- public function testConnection(string $apiKey = null): bool
+ private function getConnection(): Connection
{
- try {
- $searchParams = new SearchParameters();
- $searchRequest = new SearchFilesRequest();
- $resultColumnArray = [];
-
- $resultColumnArray[] = 'nb_results';
-
- $searchRequest->setLocale('en_GB');
- $searchRequest->setSearchParams($searchParams);
- $searchRequest->setResultColumns($resultColumnArray);
-
- $client = $this->getConnection($apiKey);
- $client->searchFilesInitialize($searchRequest, $this->getAccessToken());
-
- return (bool)$client->getNextResponse()->nb_results;
- } catch (Exception $exception) {
- $message = __(
- 'An error occurred during Adobe Stock API connection test: %error_message',
- ['error_message' => $exception->getMessage()]
- );
- $this->logger->notice($message->render());
- return false;
- }
+ return $this->connection;
}
/**
- * Handle SDK Exception and throw Magento exception instead
- *
- * @param Phrase $message
- * @param Exception $exception
- * @throws IntegrationException
+ * @inheritdoc
*/
- private function processException(Phrase $message, Exception $exception)
+ public function testConnection(string $apiKey): bool
{
- $this->logger->critical($message->render());
- throw new IntegrationException($message, $exception, $exception->getCode());
+ return $this->getConnection()->testApiKey($apiKey);
}
}
diff --git a/AdobeStockClient/Model/Connection.php b/AdobeStockClient/Model/Connection.php
new file mode 100644
index 000000000000..5f132b76e25f
--- /dev/null
+++ b/AdobeStockClient/Model/Connection.php
@@ -0,0 +1,270 @@
+clientConfig = $clientConfig;
+ $this->connectionFactory = $connectionFactory;
+ $this->imsConfig = $imsConfig;
+ $this->log = $logger;
+ $this->getAccessToken = $getAccessToken;
+ $this->flushUserTokens = $flushUserTokens;
+ $this->httpClient = $httpClient;
+ }
+
+ /**
+ * Create new SDK connection instance
+ *
+ * @param string|null $apiKey
+ * @return AdobeStock
+ */
+ private function getConnection(string $apiKey = null): AdobeStock
+ {
+ if (!$this->connection) {
+ $this->connection = $this->connectionFactory->create(
+ $apiKey ?? $this->imsConfig->getApiKey(),
+ $this->clientConfig->getProductName(),
+ $this->clientConfig->getTargetEnvironment(),
+ $this->httpClient
+ );
+ }
+ return $this->connection;
+ }
+
+ /**
+ * Checks if Access token valid and returns result.
+ *
+ * @return string|null
+ */
+ private function getAccessToken(): string
+ {
+ return $this->getAccessToken->execute();
+ }
+
+ /**
+ * Handle Adobe Stock SDK exception
+ *
+ * @param \Exception $exception
+ * @param string $message
+ * @throws AuthenticationException
+ * @throws AuthorizationException
+ * @throws IntegrationException
+ */
+ private function handleException(\Exception $exception, string $message): void
+ {
+ if (strpos($exception->getMessage(), 'Api Key is invalid') !== false) {
+ throw new AuthenticationException(__('Adobe API Key is invalid!'));
+ }
+ if (strpos($exception->getMessage(), 'Oauth token is not valid') !== false) {
+ $this->flushUserTokens->execute();
+ throw new AuthorizationException(__('Adobe API login has expired!'));
+ }
+ $phrase = __(
+ $message . ': %error_message',
+ ['error_message' => $exception->getMessage()]
+ );
+ throw new IntegrationException($phrase, $exception, $exception->getCode());
+ }
+
+ /**
+ * Test if the connection to Adobe Stock API can be established with the given API key
+ *
+ * @param string $apiKey
+ * @return bool
+ */
+ public function testApiKey(string $apiKey): bool
+ {
+ try {
+ $searchParams = new SearchParameters();
+ $searchRequest = new SearchFilesRequest();
+ $resultColumnArray = [];
+ $resultColumnArray[] = 'nb_results';
+
+ $searchRequest->setLocale('en_GB');
+ $searchRequest->setSearchParams($searchParams);
+ $searchRequest->setResultColumns($resultColumnArray);
+
+ $client = $this->getConnection($apiKey);
+ $client->searchFilesInitialize($searchRequest);
+
+ return (bool)$client->getNextResponse()->nb_results;
+ } catch (Exception $exception) {
+ return false;
+ }
+ }
+
+ /**
+ * Method to initialize search files.
+ *
+ * @param SearchFilesRequest $request
+ * @return $this
+ * @throws AuthenticationException
+ * @throws AuthorizationException
+ * @throws IntegrationException
+ */
+ public function searchFilesInitialize(SearchFilesRequest $request): self
+ {
+ try {
+ $this->getConnection()->searchFilesInitialize($request, $this->getAccessToken());
+ return $this;
+ } catch (\Exception $exception) {
+ $this->handleException($exception, 'Failed to initialize Adobe Stock search files request');
+ }
+ }
+
+ /**
+ * Get the next search files response page.
+ *
+ * @return SearchFilesResponse
+ * @throws AuthenticationException
+ * @throws AuthorizationException
+ * @throws IntegrationException
+ */
+ public function getNextResponse(): SearchFilesResponse
+ {
+ try {
+ return $this->getConnection()->getNextResponse();
+ } catch (\Exception $exception) {
+ $this->handleException($exception, 'Failed to retrieve Adobe Stock search files results');
+ }
+ }
+
+ /**
+ * Get the licensing capabilities for a user.
+ *
+ * @param LicenseRequest $request
+ * @return LicenseResponse
+ * @throws AuthenticationException
+ * @throws AuthorizationException
+ * @throws IntegrationException
+ */
+ public function getMemberProfile(LicenseRequest $request): LicenseResponse
+ {
+ try {
+ return $this->getConnection()->getMemberProfile($request, $this->getAccessToken());
+ } catch (\Exception $exception) {
+ $this->handleException($exception, 'Failed to retrieve Adobe Stock member profile');
+ }
+ }
+
+ /**
+ * Requests a license for an asset for a specific user.
+ *
+ * @param LicenseRequest $request
+ * @return LicenseResponse
+ * @throws AuthenticationException
+ * @throws AuthorizationException
+ * @throws IntegrationException
+ */
+ public function getContentLicense(LicenseRequest $request): LicenseResponse
+ {
+ try {
+ return $this->getConnection()->getContentLicense($request, $this->getAccessToken());
+ } catch (\Exception $exception) {
+ $this->handleException($exception, 'Failed to retrieve Adobe Stock content license');
+ }
+ }
+
+ /**
+ * Provide the url of the asset if it is already licensed.
+ *
+ * @param LicenseRequest $request
+ * @return string
+ * @throws AuthenticationException
+ * @throws AuthorizationException
+ * @throws IntegrationException
+ */
+ public function downloadAssetUrl(LicenseRequest $request): string
+ {
+ try {
+ return $this->getConnection()->downloadAssetUrl($request, $this->getAccessToken());
+ } catch (\Exception $exception) {
+ $this->handleException($exception, 'Failed to retrieve Adobe Stock asset download URL');
+ }
+ }
+}
diff --git a/AdobeStockClient/Model/ConnectionFactory.php b/AdobeStockClient/Model/ConnectionFactory.php
index c91fc2de27e9..9dbcf47a7315 100644
--- a/AdobeStockClient/Model/ConnectionFactory.php
+++ b/AdobeStockClient/Model/ConnectionFactory.php
@@ -9,6 +9,7 @@
namespace Magento\AdobeStockClient\Model;
use AdobeStock\Api\Client\AdobeStock;
+use AdobeStock\Api\Client\Http\HttpInterface;
/**
* Class ConnectionFactory
@@ -21,10 +22,15 @@ class ConnectionFactory
* @param string $apiKey
* @param string $productName
* @param string $targetEnvironment
+ * @param HttpInterface|null $httpClient
* @return AdobeStock
*/
- public function create(string $apiKey, string $productName, string $targetEnvironment): AdobeStock
- {
- return new AdobeStock($apiKey, $productName, $targetEnvironment);
+ public function create(
+ string $apiKey,
+ string $productName,
+ string $targetEnvironment,
+ HttpInterface $httpClient = null
+ ): AdobeStock {
+ return new AdobeStock($apiKey, $productName, $targetEnvironment, $httpClient);
}
}
diff --git a/AdobeStockClient/Test/Integration/Model/ClientTest.php b/AdobeStockClient/Test/Integration/Model/ClientTest.php
index cfa2aa4acbdc..53412639d8e8 100644
--- a/AdobeStockClient/Test/Integration/Model/ClientTest.php
+++ b/AdobeStockClient/Test/Integration/Model/ClientTest.php
@@ -8,12 +8,11 @@
namespace Magento\AdobeStockClient\Test\Integration\Model;
-use AdobeStock\Api\Client\AdobeStock;
use AdobeStock\Api\Models\StockFile;
use AdobeStock\Api\Response\SearchFiles as SearchFilesResponse;
use AdobeStock\Api\Request\SearchFiles as SearchFilesRequest;
use Magento\AdobeStockClient\Model\Client;
-use Magento\AdobeStockClient\Model\ConnectionFactory;
+use Magento\AdobeStockClient\Model\Connection;
use Magento\Framework\Api\FilterBuilder;
use Magento\Framework\Api\Search\SearchCriteriaBuilder;
use Magento\Framework\Api\Search\SearchResultInterface;
@@ -34,7 +33,7 @@ class ClientTest extends TestCase
private $client;
/**
- * @var AdobeStock|MockObject
+ * @var Connection|MockObject
*/
private $connection;
@@ -43,21 +42,14 @@ class ClientTest extends TestCase
*/
protected function setUp(): void
{
- $this->connection = $this->getMockBuilder(AdobeStock::class)
+ $this->connection = $this->getMockBuilder(Connection::class)
->setMethods(['searchFilesInitialize', 'getNextResponse'])
->disableOriginalConstructor()
->getMock();
- $connectionFactory = $this->getMockBuilder(ConnectionFactory::class)
- ->setMethods(['create'])
- ->disableOriginalConstructor()
- ->getMock();
- $connectionFactory->expects($this->once())
- ->method('create')
- ->willReturn($this->connection);
$this->client = Bootstrap::getObjectManager()->create(
Client::class,
[
- 'connectionFactory' => $connectionFactory
+ 'connection' => $this->connection
]
);
}
@@ -81,7 +73,6 @@ public function testSearch(): void
$response->expects($this->once())
->method('getNbResults')
->willReturn(3);
-
$this->connection->expects($this->once())
->method('searchFilesInitialize')
->with(
@@ -92,8 +83,7 @@ function (SearchFilesRequest $searchFiles) use ($words) {
&& in_array('nb_results', $searchFiles->getResultColumns())
&& $searchFiles->getSearchParams()->getWords() == $words;
}
- ),
- null
+ )
);
$this->connection->expects($this->once())
->method('getNextResponse')
diff --git a/AdobeStockClientApi/Api/ClientInterface.php b/AdobeStockClientApi/Api/ClientInterface.php
index 9dba2a39c401..05d0f0a2bfdb 100644
--- a/AdobeStockClientApi/Api/ClientInterface.php
+++ b/AdobeStockClientApi/Api/ClientInterface.php
@@ -44,11 +44,10 @@ public function getLicenseConfirmation(int $contentId): LicenseConfirmationInter
/**
* Perform a basic request to Adobe Stock API to check network connection, API key, etc.
*
- * @param string|null $apiKey
- *
+ * @param string $apiKey
* @return bool
*/
- public function testConnection(string $apiKey = null): bool;
+ public function testConnection(string $apiKey): bool;
/**
* Invokes licensing image operation via Adobe Stock API
@@ -62,7 +61,7 @@ public function licenseImage(int $contentId): void;
* Returns download URL for a licensed image
*
* @param int $contentId
- * @return mixed
+ * @return string
*/
public function getImageDownloadUrl(int $contentId): string;
}
diff --git a/AdobeStockImageAdminUi/Model/SignInConfigProvider.php b/AdobeStockImageAdminUi/Model/SignInConfigProvider.php
index 9c0ee4e11c05..9637925e5cf1 100644
--- a/AdobeStockImageAdminUi/Model/SignInConfigProvider.php
+++ b/AdobeStockImageAdminUi/Model/SignInConfigProvider.php
@@ -10,6 +10,8 @@
use Magento\AdobeImsApi\Api\ConfigProviderInterface;
use Magento\AdobeImsApi\Api\UserAuthorizedInterface;
use Magento\AdobeStockClientApi\Api\ClientInterface;
+use Magento\Framework\Exception\AuthenticationException;
+use Magento\Framework\Exception\AuthorizationException;
use Magento\Framework\UrlInterface;
/**
@@ -68,16 +70,23 @@ public function get(): array
*/
private function getUserQuota(): array
{
+ $defaultQuota = [
+ 'images' => 0,
+ 'credits' => 0
+ ];
if (!$this->userAuthorized->execute()) {
+ return $defaultQuota;
+ }
+ try {
+ $quota = $this->client->getQuota();
return [
- 'images' => 0,
- 'credits' => 0
+ 'images' => $quota->getImages(),
+ 'credits' => $quota->getCredits()
];
+ } catch (AuthenticationException $exception) {
+ return $defaultQuota;
+ } catch (AuthorizationException $exception) {
+ return $defaultQuota;
}
- $quota = $this->client->getQuota();
- return [
- 'images' => $quota->getImages(),
- 'credits' => $quota->getCredits()
- ];
}
}