From ec0115ecd3f037f49368b37678dc402551cda013 Mon Sep 17 00:00:00 2001 From: Karsten Dambekalns Date: Wed, 7 Sep 2022 17:28:30 +0200 Subject: [PATCH 1/5] Modernize PHP code --- Classes/AssetSource/CantoAssetProxy.php | 80 ++++--------------- Classes/AssetSource/CantoAssetProxyQuery.php | 78 ++++-------------- .../CantoAssetProxyQueryResult.php | 20 +---- .../AssetSource/CantoAssetProxyRepository.php | 33 +------- Classes/AssetSource/CantoAssetSource.php | 70 +++------------- Classes/Command/CantoCommandController.php | 2 +- Classes/Domain/Model/AccountAuthorization.php | 4 +- Classes/Package.php | 2 +- Classes/Service/AssetUpdateService.php | 11 ++- Classes/Service/CantoClient.php | 23 +----- Classes/Service/CantoOAuthClient.php | 4 +- Classes/Service/CantoOAuthProvider.php | 1 - 12 files changed, 63 insertions(+), 265 deletions(-) diff --git a/Classes/AssetSource/CantoAssetProxy.php b/Classes/AssetSource/CantoAssetProxy.php index a2d1dd5..ef142b8 100644 --- a/Classes/AssetSource/CantoAssetProxy.php +++ b/Classes/AssetSource/CantoAssetProxy.php @@ -36,65 +36,18 @@ */ final class CantoAssetProxy implements AssetProxyInterface, HasRemoteOriginalInterface, SupportsIptcMetadataInterface { - /** - * @var CantoAssetSource - */ - private $assetSource; - - /** - * @var string - */ - private $identifier; - - /** - * @var string - */ - private $label; - - /** - * @var string - */ - private $filename; - - /** - * @var \DateTime - */ - private $lastModified; - - /** - * @var int - */ - private $fileSize; - - /** - * @var string - */ - private $mediaType; - - /** - * @var array - */ - private $iptcProperties = []; - - /** - * @var string - */ - private $previewUri; - - /** - * @var int - */ - private $widthInPixels; - - /** - * @var int - */ - private $heightInPixels; - - /** - * @var array - */ - private $tags = []; + private CantoAssetSource $assetSource; + private string $identifier; + private string $label; + private string $filename; + private \DateTime $lastModified; + private int $fileSize; + private string $mediaType; + private array $iptcProperties = []; + private string $previewUri; + private int $widthInPixels; + private int $heightInPixels; + private array $tags = []; /** * @Flow\Inject @@ -198,7 +151,6 @@ public function getHeightInPixels(): ?int } /** - * @return UriInterface * @throws ThumbnailServiceException */ public function getThumbnailUri(): ?UriInterface @@ -206,13 +158,12 @@ public function getThumbnailUri(): ?UriInterface $thumbnailConfiguration = $this->thumbnailService->getThumbnailConfigurationForPreset('Neos.Media.Browser:Thumbnail'); return new Uri(sprintf( '%s/%d', - preg_replace('|/[0-9]+$|', '', $this->previewUri), + preg_replace('|/\d+$|', '', $this->previewUri), max($thumbnailConfiguration->getMaximumWidth(), $thumbnailConfiguration->getMaximumHeight()) )); } /** - * @return UriInterface * @throws ThumbnailServiceException */ public function getPreviewUri(): ?UriInterface @@ -220,7 +171,7 @@ public function getPreviewUri(): ?UriInterface $previewConfiguration = $this->thumbnailService->getThumbnailConfigurationForPreset('Neos.Media.Browser:Preview'); return new Uri(sprintf( '%s/%d', - preg_replace('|/[0-9]+$|', '', $this->previewUri), + preg_replace('|/\d+$|', '', $this->previewUri), max($previewConfiguration->getMaximumWidth(), $previewConfiguration->getMaximumHeight()) )); } @@ -236,9 +187,6 @@ public function getImportStream() return fopen((string)$this->assetSource->getCantoClient()->directUri($this->identifier), 'rb'); } - /** - * @return string - */ public function getLocalAssetIdentifier(): ?string { $importedAsset = $this->importedAssetRepository->findOneByAssetSourceIdentifierAndRemoteAssetIdentifier($this->assetSource->getIdentifier(), $this->identifier); diff --git a/Classes/AssetSource/CantoAssetProxyQuery.php b/Classes/AssetSource/CantoAssetProxyQuery.php index 1406ece..253625e 100644 --- a/Classes/AssetSource/CantoAssetProxyQuery.php +++ b/Classes/AssetSource/CantoAssetProxyQuery.php @@ -31,51 +31,19 @@ */ final class CantoAssetProxyQuery implements AssetProxyQueryInterface { - /** - * @var string - */ - private $searchTerm = ''; - - /** - * @var Tag - */ - private $activeTag; - - /** - * @var string - */ - private $tagQuery = ''; - - /** - * @var AssetCollection - */ - private $activeAssetCollection; - - /** - * @var string - */ - private $assetTypeFilter = 'All'; - - /** - * @var array - */ - private $orderings = []; - - /** - * @var int - */ - private $offset = 0; - - /** - * @var int - */ - private $limit = 30; + private string $searchTerm = ''; + private ?Tag $activeTag = null; + private string $tagQuery = ''; + private ?AssetCollection $activeAssetCollection = null; + private string $assetTypeFilter = 'All'; + private array $orderings = []; + private int $offset = 0; + private int $limit = 30; /** * @Flow\InjectConfiguration(path="mapping", package="Flownative.Canto") - * @var array */ - protected $mapping = []; + protected array $mapping = []; /** * @Flow\Inject @@ -83,9 +51,6 @@ final class CantoAssetProxyQuery implements AssetProxyQueryInterface */ protected $logger; - /** - * @param CantoAssetSource $assetSource - */ public function __construct(private CantoAssetSource $assetSource) { } @@ -221,24 +186,13 @@ private function sendSearchRequest(int $limit, array $orderings): Response $searchTerm = $this->searchTerm; - switch ($this->assetTypeFilter) { - case 'Image': - $formatTypes = ['image']; - break; - case 'Video': - $formatTypes = ['video']; - break; - case 'Audio': - $formatTypes = ['audio']; - break; - case 'Document': - $formatTypes = ['document']; - break; - case 'All': - default: - $formatTypes = ['image', 'video', 'audio', 'document', 'presentation', 'other']; - break; - } + $formatTypes = match ($this->assetTypeFilter) { + 'Image' => ['image'], + 'Video' => ['video'], + 'Audio' => ['audio'], + 'Document' => ['document'], + default => ['image', 'video', 'audio', 'document', 'presentation', 'other'], + }; return $this->assetSource->getCantoClient()->search($searchTerm, $formatTypes, $this->tagQuery, $this->offset, $limit, $orderings); } diff --git a/Classes/AssetSource/CantoAssetProxyQueryResult.php b/Classes/AssetSource/CantoAssetProxyQueryResult.php index c03dcfa..47c8597 100644 --- a/Classes/AssetSource/CantoAssetProxyQueryResult.php +++ b/Classes/AssetSource/CantoAssetProxyQueryResult.php @@ -27,24 +27,10 @@ */ class CantoAssetProxyQueryResult implements AssetProxyQueryResultInterface { - /** - * @var array - */ - private $assetProxies; - - /** - * @var int - */ - private $numberOfAssetProxies; + private ?array $assetProxies = null; + private ?int $numberOfAssetProxies = null; + private \ArrayIterator $assetProxiesIterator; - /** - * @var \ArrayIterator - */ - private $assetProxiesIterator; - - /** - * @param CantoAssetProxyQuery $query - */ public function __construct(private CantoAssetProxyQuery $query) { } diff --git a/Classes/AssetSource/CantoAssetProxyRepository.php b/Classes/AssetSource/CantoAssetProxyRepository.php index 60d2080..b3c4e18 100644 --- a/Classes/AssetSource/CantoAssetProxyRepository.php +++ b/Classes/AssetSource/CantoAssetProxyRepository.php @@ -36,28 +36,14 @@ */ class CantoAssetProxyRepository implements AssetProxyRepositoryInterface, SupportsSortingInterface, SupportsTaggingInterface, SupportsCollectionsInterface { - /** - * @var AssetCollection - */ - private $activeAssetCollection; + private ?AssetCollection $activeAssetCollection = null; + private string $assetTypeFilter = 'All'; + private array $orderings = []; - /** - * @param CantoAssetSource $assetSource - */ public function __construct(private CantoAssetSource $assetSource) { } - /** - * @var string - */ - private $assetTypeFilter = 'All'; - - /** - * @var array - */ - private $orderings = []; - /** * @throws AssetNotFoundException * @throws OAuthClientException @@ -90,9 +76,6 @@ public function getAssetProxy(string $identifier): AssetProxyInterface return CantoAssetProxy::fromJsonObject($responseObject, $this->assetSource); } - /** - * @param AssetTypeFilter|null $assetType - */ public function filterByType(AssetTypeFilter $assetType = null): void { $this->assetTypeFilter = (string)$assetType ?: 'All'; @@ -147,8 +130,7 @@ public function findUntagged(): AssetProxyQueryResultInterface */ public function countAll(): int { - $query = new CantoAssetProxyQuery($this->assetSource); - return $query->count(); + return (new CantoAssetProxyQuery($this->assetSource))->count(); } /** @@ -181,10 +163,6 @@ public function countUntagged(): int return $query->count(); } - /** - * @param Tag $tag - * @return int - */ public function countByTag(Tag $tag): int { try { @@ -194,9 +172,6 @@ public function countByTag(Tag $tag): int } } - /** - * @param AssetCollection|null $assetCollection - */ public function filterByCollection(AssetCollection $assetCollection = null): void { $this->activeAssetCollection = $assetCollection; diff --git a/Classes/AssetSource/CantoAssetSource.php b/Classes/AssetSource/CantoAssetSource.php index 67255e8..e319d1b 100644 --- a/Classes/AssetSource/CantoAssetSource.php +++ b/Classes/AssetSource/CantoAssetSource.php @@ -27,55 +27,16 @@ class CantoAssetSource implements AssetSourceInterface { public const ASSET_SOURCE_IDENTIFIER = 'flownative-canto'; - /** - * @var bool - */ - private $autoTaggingEnabled = false; - - /** - * @var string - */ - private $autoTaggingInUseTag = 'used-by-neos'; - - /** - * @var string - */ - private $assetSourceIdentifier; - - /** - * @var CantoAssetProxyRepository - */ - private $assetProxyRepository; - - /** - * @var string - */ - private $apiBaseUri; - - /** - * @var string - */ - private $appId; - - /** - * @var string - */ - private $appSecret; - - /** - * @var CantoClient - */ - private $cantoClient; - - /** - * @var string - */ - private $iconPath; - - /** - * @var string - */ - private $description; + private bool $autoTaggingEnabled = false; + private string $autoTaggingInUseTag = 'used-by-neos'; + private string $assetSourceIdentifier; + private ?CantoAssetProxyRepository $assetProxyRepository = null; + private string $apiBaseUri; + private string $appId = ''; + private string $appSecret = ''; + private ?CantoClient $cantoClient = null; + private string $iconPath; + private string $description; /** * @Flow\Inject @@ -88,10 +49,6 @@ class CantoAssetSource implements AssetSourceInterface */ protected $assetProxyCache; - /** - * @param string $assetSourceIdentifier - * @param array $assetSourceOptions - */ public function __construct(string $assetSourceIdentifier, private array $assetSourceOptions) { if (preg_match('/^[a-z][a-z0-9-]{0,62}[a-z]$/', $assetSourceIdentifier) !== 1) { @@ -139,7 +96,7 @@ public function __construct(string $assetSourceIdentifier, private array $assetS } } - if ($this->appId === null || $this->appSecret === null) { + if ($this->appId === '' || $this->appSecret === '') { throw new \InvalidArgumentException(sprintf('No app id or app secret specified for Canto asset source "%s".', $assetSourceIdentifier), 1632468673); } } @@ -159,10 +116,7 @@ public function getLabel(): string return 'Canto'; } - /** - * @return CantoAssetProxyRepository - */ - public function getAssetProxyRepository(): AssetProxyRepositoryInterface + public function getAssetProxyRepository(): AssetProxyRepositoryInterface|CantoAssetProxyRepository { if ($this->assetProxyRepository === null) { $this->assetProxyRepository = new CantoAssetProxyRepository($this); diff --git a/Classes/Command/CantoCommandController.php b/Classes/Command/CantoCommandController.php index 519e6ef..91cb4f0 100644 --- a/Classes/Command/CantoCommandController.php +++ b/Classes/Command/CantoCommandController.php @@ -51,7 +51,7 @@ class CantoCommandController extends CommandController * @Flow\InjectConfiguration(path="mapping", package="Flownative.Canto") * @var array */ - protected $mapping = []; + protected array $mapping = []; /** * Tag used assets diff --git a/Classes/Domain/Model/AccountAuthorization.php b/Classes/Domain/Model/AccountAuthorization.php index 2c0b81f..f251075 100644 --- a/Classes/Domain/Model/AccountAuthorization.php +++ b/Classes/Domain/Model/AccountAuthorization.php @@ -25,12 +25,12 @@ class AccountAuthorization * @Identity() * @var string */ - protected $flowAccountIdentifier; + protected string $flowAccountIdentifier; /** * @var string */ - protected $authorizationId; + protected string $authorizationId; public function getFlowAccountIdentifier(): string { diff --git a/Classes/Package.php b/Classes/Package.php index 2ee5294..a462725 100644 --- a/Classes/Package.php +++ b/Classes/Package.php @@ -27,7 +27,7 @@ class Package extends BasePackage * @param Bootstrap $bootstrap The current bootstrap * @return void */ - public function boot(Bootstrap $bootstrap) + public function boot(Bootstrap $bootstrap): void { $dispatcher = $bootstrap->getSignalSlotDispatcher(); $dispatcher->connect( diff --git a/Classes/Service/AssetUpdateService.php b/Classes/Service/AssetUpdateService.php index 181db6c..d50c29d 100644 --- a/Classes/Service/AssetUpdateService.php +++ b/Classes/Service/AssetUpdateService.php @@ -20,7 +20,6 @@ use Neos\Flow\Persistence\PersistenceManagerInterface; use Neos\Flow\ResourceManagement\ResourceManager; use Neos\Media\Domain\Model\AssetInterface; -use Neos\Media\Domain\Model\AssetSource\AssetSourceInterface; use Neos\Media\Domain\Model\ImageVariant; use Neos\Media\Domain\Repository\AssetRepository; use Neos\Media\Domain\Repository\ImportedAssetRepository; @@ -121,7 +120,7 @@ public function handleAssetMetadataUpdated(array $payload): bool $this->persistenceManager->persistAll(); return true; - } catch (\Exception) { + } catch (\Throwable) { return false; } } @@ -142,7 +141,7 @@ public function handleNewAssetVersionAdded(array $payload): bool $this->persistenceManager->persistAll(); return true; - } catch (\Exception) { + } catch (\Throwable) { return false; } } @@ -166,7 +165,7 @@ private function replaceAsset(string $identifier): void $proxy = $this->getAssetSource()->getAssetProxyRepository()->getAssetProxy($identifier); $newResource = $this->resourceManager->importResource($proxy->getImportStream()); } catch (\Exception $e) { - $this->logger->debug(sprintf('Could not import resource for asset %s from %s, exception: %s', $localAssetIdentifier, $identifier, $this->throwableStorage->logThrowable($e)), LogEnvironment::fromMethodName(__METHOD__));; + $this->logger->debug(sprintf('Could not import resource for asset %s from %s, exception: %s', $localAssetIdentifier, $identifier, $this->throwableStorage->logThrowable($e)), LogEnvironment::fromMethodName(__METHOD__)); throw $e; } $newResource->setFilename($proxy->getFilename()); @@ -174,7 +173,7 @@ private function replaceAsset(string $identifier): void $this->logger->debug(sprintf('Replaced resource %s with %s on asset %s from Canto asset %s', $localAsset->getResource()->getSha1(), $newResource->getSha1(), $localAssetIdentifier, $identifier), LogEnvironment::fromMethodName(__METHOD__)); // … to delete it here! - $this->logger->debug(sprintf('Trying to delete replaced resource: %s (%s)', $previousResource->getFilename(), $previousResource->getSha1()), LogEnvironment::fromMethodName(__METHOD__));; + $this->logger->debug(sprintf('Trying to delete replaced resource: %s (%s)', $previousResource->getFilename(), $previousResource->getSha1()), LogEnvironment::fromMethodName(__METHOD__)); $this->resourceManager->deleteResource($previousResource); } @@ -195,7 +194,7 @@ private function flushProxyForAsset(string $identifier): void } } - private function getAssetSource(): AssetSourceInterface + private function getAssetSource(): CantoAssetSource { /** @var CantoAssetSource $assetSource */ $assetSource = $this->assetSourceService->getAssetSources()[CantoAssetSource::ASSET_SOURCE_IDENTIFIER]; diff --git a/Classes/Service/CantoClient.php b/Classes/Service/CantoClient.php index c1cd7e2..6018c82 100644 --- a/Classes/Service/CantoClient.php +++ b/Classes/Service/CantoClient.php @@ -47,6 +47,9 @@ final class CantoClient { protected bool $allowClientCredentialsAuthentication = false; + private ?Authorization $authorization = null; + private Client $httpClient; + /** * @Flow\Inject * @var Bootstrap @@ -71,22 +74,6 @@ final class CantoClient */ protected $accountAuthorizationRepository; - /** - * @var Authorization - */ - private $authorization; - - /** - * @var Client - */ - private $httpClient; - - /** - * @param string $apiBaseUri - * @param string $appId - * @param string $appSecret - * @param string $serviceName - */ public function __construct(private string $apiBaseUri, protected string $appId, protected string $appSecret, private string $serviceName) { $this->httpClient = new Client(['allow_redirects' => true]); @@ -316,10 +303,6 @@ public function directUri(string $assetProxyId): ?Uri /** * Returns a prepared request to an OAuth 2.0 service provider using Bearer token authentication * - * @param Authorization $authorization - * @param string $uriPathAndQuery A relative URI of the web server, prepended by the base URI - * @param string $method The HTTP method, for example "GET" or "POST" - * @param array $bodyFields Associative array of body fields to send (optional) * @throws OAuthClientException */ private function getAuthenticatedRequest(Authorization $authorization, string $uriPathAndQuery, string $method = 'GET', array $bodyFields = []): RequestInterface diff --git a/Classes/Service/CantoOAuthClient.php b/Classes/Service/CantoOAuthClient.php index 2f1f260..7009a17 100644 --- a/Classes/Service/CantoOAuthClient.php +++ b/Classes/Service/CantoOAuthClient.php @@ -126,7 +126,7 @@ public function finishAuthorization(string $stateIdentifier, string $code, strin { $stateFromCache = $this->stateCache->get($stateIdentifier); if (empty($stateFromCache)) { - throw new OAuthClientException(sprintf('OAuth2 (%s): Finishing authorization failed because oAuth state %s could not be retrieved from the state cache.', CantoOAuthClient::getServiceType(), $stateIdentifier), 1627046882); + throw new OAuthClientException(sprintf('OAuth2 (%s): Finishing authorization failed because oAuth state %s could not be retrieved from the state cache.', self::getServiceType(), $stateIdentifier), 1627046882); } $authorizationId = $stateFromCache['authorizationId']; @@ -149,7 +149,7 @@ public function finishAuthorization(string $stateIdentifier, string $code, strin $accountAuthorization->setAuthorizationId($authorizationId); $this->persistenceManager->allowObject($accountAuthorization); - $queryParameterName = CantoOAuthClient::generateAuthorizationIdQueryParameterName(CantoAssetSource::ASSET_SOURCE_IDENTIFIER); + $queryParameterName = self::generateAuthorizationIdQueryParameterName(CantoAssetSource::ASSET_SOURCE_IDENTIFIER); $queryParameters = UriHelper::parseQueryIntoArguments($returnUri); unset($queryParameters[$queryParameterName]); return UriHelper::uriWithArguments($returnUri, $queryParameters); diff --git a/Classes/Service/CantoOAuthProvider.php b/Classes/Service/CantoOAuthProvider.php index b6beeba..5e3c889 100644 --- a/Classes/Service/CantoOAuthProvider.php +++ b/Classes/Service/CantoOAuthProvider.php @@ -27,7 +27,6 @@ final class CantoOAuthProvider extends GenericProvider * Requests an access token using a specified grant and option set. * * @param mixed $grant - * @return AccessTokenInterface * @throws IdentityProviderException */ public function getAccessToken($grant, array $options = []): AccessTokenInterface From d1a5845a31de8fe446aa018d89b0a96233cd7dda Mon Sep 17 00:00:00 2001 From: Karsten Dambekalns Date: Wed, 7 Sep 2022 17:31:19 +0200 Subject: [PATCH 2/5] Remove unused code --- Classes/Command/CantoCommandController.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/Classes/Command/CantoCommandController.php b/Classes/Command/CantoCommandController.php index 91cb4f0..0ad194b 100644 --- a/Classes/Command/CantoCommandController.php +++ b/Classes/Command/CantoCommandController.php @@ -76,8 +76,6 @@ public function tagUsedAssetsCommand(string $assetSource = CantoAssetSource::ASS $this->quit(1); } - $assetProxyRepository = $cantoAssetSource->getAssetProxyRepository(); - assert($assetProxyRepository instanceof CantoAssetProxyRepository); $cantoAssetSource->getAssetProxyCache()->flush(); foreach ($this->assetRepository->iterate($iterator) as $asset) { From 0c73b5f8f50648b1122add7edebf47785df6076a Mon Sep 17 00:00:00 2001 From: Karsten Dambekalns Date: Wed, 7 Sep 2022 17:29:53 +0200 Subject: [PATCH 3/5] Fix @throws annotations --- Classes/AssetSource/CantoAssetProxy.php | 9 ++- Classes/AssetSource/CantoAssetProxyQuery.php | 42 +++++++++++--- .../CantoAssetProxyQueryResult.php | 56 +++++++++++++++++++ .../AssetSource/CantoAssetProxyRepository.php | 32 +++++++++-- Classes/Command/CantoCommandController.php | 11 +++- .../Controller/AuthorizationController.php | 17 ++++++ Classes/Service/AssetUpdateService.php | 15 +++++ Classes/Service/CantoClient.php | 7 +++ Classes/Service/CantoOAuthClient.php | 17 ++++++ 9 files changed, 190 insertions(+), 16 deletions(-) diff --git a/Classes/AssetSource/CantoAssetProxy.php b/Classes/AssetSource/CantoAssetProxy.php index ef142b8..97ab72b 100644 --- a/Classes/AssetSource/CantoAssetProxy.php +++ b/Classes/AssetSource/CantoAssetProxy.php @@ -15,10 +15,13 @@ use Exception; use Flownative\Canto\Exception\AuthenticationFailedException; +use Flownative\Canto\Exception\MissingClientSecretException; use Flownative\OAuth2\Client\OAuthClientException; use GuzzleHttp\Exception\GuzzleException; use GuzzleHttp\Psr7\Uri; +use League\OAuth2\Client\Provider\Exception\IdentityProviderException; use Neos\Flow\Annotations as Flow; +use Neos\Flow\Mvc\Routing\Exception\MissingActionNameException; use Neos\Media\Domain\Model\AssetSource\AssetProxy\AssetProxyInterface; use Neos\Media\Domain\Model\AssetSource\AssetProxy\HasRemoteOriginalInterface; use Neos\Media\Domain\Model\AssetSource\AssetProxy\SupportsIptcMetadataInterface; @@ -179,8 +182,12 @@ public function getPreviewUri(): ?UriInterface /** * @return resource * @throws AuthenticationFailedException - * @throws OAuthClientException * @throws GuzzleException + * @throws OAuthClientException + * @throws MissingClientSecretException + * @throws IdentityProviderException + * @throws \Neos\Flow\Http\Exception + * @throws MissingActionNameException */ public function getImportStream() { diff --git a/Classes/AssetSource/CantoAssetProxyQuery.php b/Classes/AssetSource/CantoAssetProxyQuery.php index 253625e..1b58e0a 100644 --- a/Classes/AssetSource/CantoAssetProxyQuery.php +++ b/Classes/AssetSource/CantoAssetProxyQuery.php @@ -14,12 +14,16 @@ */ use Flownative\Canto\Exception\AuthenticationFailedException; +use Flownative\Canto\Exception\MissingClientSecretException; use Flownative\OAuth2\Client\OAuthClientException; use GuzzleHttp\Exception\GuzzleException; use GuzzleHttp\Psr7\Response; +use League\OAuth2\Client\Provider\Exception\IdentityProviderException; use Neos\Cache\Exception as CacheException; use Neos\Cache\Exception\InvalidDataException; use Neos\Flow\Annotations as Flow; +use Neos\Flow\Http\Exception; +use Neos\Flow\Mvc\Routing\Exception\MissingActionNameException; use Neos\Media\Domain\Model\AssetCollection; use Neos\Media\Domain\Model\AssetSource\AssetProxyQueryInterface; use Neos\Media\Domain\Model\AssetSource\AssetProxyQueryResultInterface; @@ -134,9 +138,14 @@ public function execute(): AssetProxyQueryResultInterface } /** - * @throws OAuthClientException - * @throws GuzzleException * @throws AuthenticationFailedException + * @throws Exception + * @throws GuzzleException + * @throws IdentityProviderException + * @throws MissingActionNameException + * @throws MissingClientSecretException + * @throws OAuthClientException + * @throws \JsonException */ public function count(): int { @@ -153,6 +162,8 @@ public function count(): int * @throws CacheException * @throws InvalidDataException * @throws AuthenticationFailedException + * @throws \JsonException + * @throws \Exception */ public function getArrayResult(): array { @@ -174,9 +185,14 @@ public function getArrayResult(): array } /** - * @throws OAuthClientException - * @throws GuzzleException * @throws AuthenticationFailedException + * @throws Exception + * @throws GuzzleException + * @throws IdentityProviderException + * @throws MissingActionNameException + * @throws MissingClientSecretException + * @throws OAuthClientException + * @throws \JsonException */ private function sendSearchRequest(int $limit, array $orderings): Response { @@ -197,9 +213,14 @@ private function sendSearchRequest(int $limit, array $orderings): Response } /** - * @throws OAuthClientException - * @throws GuzzleException * @throws AuthenticationFailedException + * @throws Exception + * @throws GuzzleException + * @throws IdentityProviderException + * @throws MissingActionNameException + * @throws MissingClientSecretException + * @throws OAuthClientException + * @throws \JsonException */ public function prepareTagQuery(): void { @@ -230,9 +251,14 @@ public function prepareTagQuery(): void } /** - * @throws OAuthClientException - * @throws GuzzleException * @throws AuthenticationFailedException + * @throws GuzzleException + * @throws OAuthClientException + * @throws MissingClientSecretException + * @throws \JsonException + * @throws IdentityProviderException + * @throws Exception + * @throws MissingActionNameException */ public function prepareUntaggedQuery(): void { diff --git a/Classes/AssetSource/CantoAssetProxyQueryResult.php b/Classes/AssetSource/CantoAssetProxyQueryResult.php index 47c8597..844948e 100644 --- a/Classes/AssetSource/CantoAssetProxyQueryResult.php +++ b/Classes/AssetSource/CantoAssetProxyQueryResult.php @@ -82,48 +82,104 @@ public function toArray(): array return $this->assetProxies; } + /** + * @throws AuthenticationFailedException + * @throws OAuthClientException + * @throws CacheException + * @throws GuzzleException + * @throws InvalidDataException + */ public function current() { $this->initialize(); return $this->assetProxiesIterator->current(); } + /** + * @throws OAuthClientException + * @throws AuthenticationFailedException + * @throws CacheException + * @throws GuzzleException + * @throws InvalidDataException + */ public function next() { $this->initialize(); $this->assetProxiesIterator->next(); } + /** + * @throws OAuthClientException + * @throws AuthenticationFailedException + * @throws CacheException + * @throws GuzzleException + * @throws InvalidDataException + */ public function key() { $this->initialize(); return $this->assetProxiesIterator->key(); } + /** + * @throws OAuthClientException + * @throws AuthenticationFailedException + * @throws CacheException + * @throws GuzzleException + * @throws InvalidDataException + */ public function valid(): bool { $this->initialize(); return $this->assetProxiesIterator->valid(); } + /** + * @throws OAuthClientException + * @throws AuthenticationFailedException + * @throws CacheException + * @throws GuzzleException + * @throws InvalidDataException + */ public function rewind() { $this->initialize(); $this->assetProxiesIterator->rewind(); } + /** + * @throws AuthenticationFailedException + * @throws OAuthClientException + * @throws CacheException + * @throws GuzzleException + * @throws InvalidDataException + */ public function offsetExists($offset): bool { $this->initialize(); return $this->assetProxiesIterator->offsetExists($offset); } + /** + * @throws AuthenticationFailedException + * @throws OAuthClientException + * @throws CacheException + * @throws GuzzleException + * @throws InvalidDataException + */ public function offsetGet($offset) { $this->initialize(); return $this->assetProxiesIterator->offsetGet($offset); } + /** + * @throws AuthenticationFailedException + * @throws OAuthClientException + * @throws CacheException + * @throws GuzzleException + * @throws InvalidDataException + */ public function offsetSet($offset, $value) { $this->initialize(); diff --git a/Classes/AssetSource/CantoAssetProxyRepository.php b/Classes/AssetSource/CantoAssetProxyRepository.php index b3c4e18..e73e86c 100644 --- a/Classes/AssetSource/CantoAssetProxyRepository.php +++ b/Classes/AssetSource/CantoAssetProxyRepository.php @@ -15,11 +15,15 @@ use Flownative\Canto\Exception\AssetNotFoundException; use Flownative\Canto\Exception\AuthenticationFailedException; +use Flownative\Canto\Exception\MissingClientSecretException; use Flownative\OAuth2\Client\OAuthClientException; use GuzzleHttp\Exception\GuzzleException; use GuzzleHttp\Utils; +use League\OAuth2\Client\Provider\Exception\IdentityProviderException; use Neos\Cache\Exception as CacheException; use Neos\Cache\Exception\InvalidDataException; +use Neos\Flow\Http\Exception; +use Neos\Flow\Mvc\Routing\Exception\MissingActionNameException; use Neos\Media\Domain\Model\AssetCollection; use Neos\Media\Domain\Model\AssetSource\AssetProxy\AssetProxyInterface; use Neos\Media\Domain\Model\AssetSource\AssetProxyQueryResultInterface; @@ -109,9 +113,15 @@ public function findByTag(Tag $tag): AssetProxyQueryResultInterface } /** - * @throws OAuthClientException - * @throws GuzzleException + * @return AssetProxyQueryResultInterface * @throws AuthenticationFailedException + * @throws Exception + * @throws GuzzleException + * @throws IdentityProviderException + * @throws MissingActionNameException + * @throws MissingClientSecretException + * @throws OAuthClientException + * @throws \JsonException */ public function findUntagged(): AssetProxyQueryResultInterface { @@ -124,9 +134,14 @@ public function findUntagged(): AssetProxyQueryResultInterface } /** - * @throws OAuthClientException - * @throws GuzzleException * @throws AuthenticationFailedException + * @throws Exception + * @throws GuzzleException + * @throws IdentityProviderException + * @throws MissingActionNameException + * @throws MissingClientSecretException + * @throws OAuthClientException + * @throws \JsonException */ public function countAll(): int { @@ -149,9 +164,14 @@ public function orderBy(array $orderings): void } /** - * @throws OAuthClientException - * @throws GuzzleException * @throws AuthenticationFailedException + * @throws GuzzleException + * @throws OAuthClientException + * @throws MissingClientSecretException + * @throws \JsonException + * @throws IdentityProviderException + * @throws Exception + * @throws MissingActionNameException */ public function countUntagged(): int { diff --git a/Classes/Command/CantoCommandController.php b/Classes/Command/CantoCommandController.php index 0ad194b..334ed1c 100644 --- a/Classes/Command/CantoCommandController.php +++ b/Classes/Command/CantoCommandController.php @@ -7,11 +7,15 @@ use Flownative\Canto\AssetSource\CantoAssetProxyRepository; use Flownative\Canto\AssetSource\CantoAssetSource; use Flownative\Canto\Exception\AuthenticationFailedException; +use Flownative\Canto\Exception\MissingClientSecretException; use Flownative\OAuth2\Client\OAuthClientException; use GuzzleHttp\Exception\GuzzleException; +use League\OAuth2\Client\Provider\Exception\IdentityProviderException; use Neos\Flow\Annotations as Flow; use Neos\Flow\Cli\CommandController; use Neos\Flow\Cli\Exception\StopCommandException; +use Neos\Flow\Http\Exception; +use Neos\Flow\Mvc\Routing\Exception\MissingActionNameException; use Neos\Flow\Persistence\Exception\IllegalObjectTypeException; use Neos\Media\Domain\Model\Asset; use Neos\Media\Domain\Model\AssetCollection; @@ -126,11 +130,16 @@ public function tagUsedAssetsCommand(string $assetSource = CantoAssetSource::ASS * * @param string $assetSourceIdentifier Name of the canto asset source * @param bool $quiet If set, only errors will be displayed. + * @throws AuthenticationFailedException * @throws GuzzleException * @throws IllegalObjectTypeException * @throws OAuthClientException * @throws StopCommandException - * @throws AuthenticationFailedException + * @throws MissingClientSecretException + * @throws \JsonException + * @throws IdentityProviderException + * @throws Exception + * @throws MissingActionNameException */ public function importCustomFieldsAsCollectionsAndTagsCommand(string $assetSourceIdentifier = CantoAssetSource::ASSET_SOURCE_IDENTIFIER, bool $quiet = true): void { diff --git a/Classes/Controller/AuthorizationController.php b/Classes/Controller/AuthorizationController.php index 86259c5..afdd6e5 100644 --- a/Classes/Controller/AuthorizationController.php +++ b/Classes/Controller/AuthorizationController.php @@ -18,7 +18,11 @@ use Flownative\OAuth2\Client\OAuthClientException; use GuzzleHttp\Psr7\Uri; use Neos\Flow\Annotations as Flow; +use Neos\Flow\Http\Exception; use Neos\Flow\Mvc\Controller\ActionController; +use Neos\Flow\Mvc\Exception\UnsupportedRequestTypeException; +use Neos\Flow\Mvc\Routing\Exception\MissingActionNameException; +use Neos\Flow\Persistence\Exception\IllegalObjectTypeException; use Neos\Flow\Security\Context; use Neos\Media\Domain\Service\AssetSourceService; @@ -36,12 +40,23 @@ class AuthorizationController extends ActionController */ protected $assetSourceService; + /** + * @throws Exception + * @throws MissingActionNameException + */ public function neededAction(string $returnUri): void { $this->view->assign('startUri', $this->uriBuilder->uriFor('start')); $this->view->assign('returnUri', $returnUri); } + /** + * @throws OAuthClientException + * @throws MissingActionNameException + * @throws Exception + * @throws UnsupportedRequestTypeException + * @throws \JsonException + */ public function startAction(): void { $appId = $this->assetSourceService->getAssetSources()[CantoAssetSource::ASSET_SOURCE_IDENTIFIER]->getAppId(); @@ -73,6 +88,8 @@ public function startAction(): void * Finish OAuth2 authorization * * @throws OAuthClientException + * @throws \JsonException + * @throws IllegalObjectTypeException */ public function finishAction(string $state, string $code, string $scope = ''): void { diff --git a/Classes/Service/AssetUpdateService.php b/Classes/Service/AssetUpdateService.php index d50c29d..688e91b 100644 --- a/Classes/Service/AssetUpdateService.php +++ b/Classes/Service/AssetUpdateService.php @@ -14,6 +14,12 @@ */ use Flownative\Canto\AssetSource\CantoAssetSource; +use Flownative\Canto\Exception\AssetNotFoundException; +use Flownative\Canto\Exception\AuthenticationFailedException; +use Flownative\OAuth2\Client\OAuthClientException; +use GuzzleHttp\Exception\GuzzleException; +use Neos\Cache\Exception; +use Neos\Cache\Exception\InvalidDataException; use Neos\Flow\Annotations as Flow; use Neos\Flow\Log\ThrowableStorageInterface; use Neos\Flow\Log\Utility\LogEnvironment; @@ -146,6 +152,15 @@ public function handleNewAssetVersionAdded(array $payload): bool } } + /** + * @throws OAuthClientException + * @throws AuthenticationFailedException + * @throws Exception + * @throws AssetNotFoundException + * @throws \Neos\Flow\ResourceManagement\Exception + * @throws GuzzleException + * @throws InvalidDataException + */ private function replaceAsset(string $identifier): void { $importedAsset = $this->importedAssetRepository->findOneByAssetSourceIdentifierAndRemoteAssetIdentifier(CantoAssetSource::ASSET_SOURCE_IDENTIFIER, $identifier); diff --git a/Classes/Service/CantoClient.php b/Classes/Service/CantoClient.php index 6018c82..0c7fb67 100644 --- a/Classes/Service/CantoClient.php +++ b/Classes/Service/CantoClient.php @@ -152,6 +152,7 @@ private function redirectToUri(string $uri): void * @throws MissingClientSecretException * @throws OAuthClientException * @throws IdentityProviderException + * @throws \JsonException */ public function getFile(string $assetProxyId): ResponseInterface { @@ -175,6 +176,7 @@ public function updateFile(string $id, array $metadata): ResponseInterface * @throws MissingActionNameException * @throws MissingClientSecretException * @throws OAuthClientException + * @throws \JsonException */ public function search(string $keyword, array $formatTypes, string $customQueryPart = '', int $offset = 0, int $limit = 50, array $orderings = []): ResponseInterface { @@ -212,6 +214,7 @@ public function search(string $keyword, array $formatTypes, string $customQueryP * @throws MissingActionNameException * @throws MissingClientSecretException * @throws OAuthClientException + * @throws \JsonException * @todo perhaps cache the result */ public function getCustomFields(): array @@ -231,6 +234,7 @@ public function getCustomFields(): array * @throws MissingActionNameException * @throws MissingClientSecretException * @throws OAuthClientException + * @throws \JsonException */ public function user(): array { @@ -249,6 +253,7 @@ public function user(): array * @throws MissingActionNameException * @throws MissingClientSecretException * @throws OAuthClientException + * @throws \JsonException */ public function tree(): array { @@ -304,6 +309,7 @@ public function directUri(string $assetProxyId): ?Uri * Returns a prepared request to an OAuth 2.0 service provider using Bearer token authentication * * @throws OAuthClientException + * @throws \JsonException */ private function getAuthenticatedRequest(Authorization $authorization, string $uriPathAndQuery, string $method = 'GET', array $bodyFields = []): RequestInterface { @@ -333,6 +339,7 @@ private function getAuthenticatedRequest(Authorization $authorization, string $u * @throws MissingActionNameException * @throws MissingClientSecretException * @throws OAuthClientException + * @throws \JsonException */ public function sendAuthenticatedRequest(string $uriPathAndQuery, string $method = 'GET', array $bodyFields = []): Response { diff --git a/Classes/Service/CantoOAuthClient.php b/Classes/Service/CantoOAuthClient.php index 7009a17..8d3a120 100644 --- a/Classes/Service/CantoOAuthClient.php +++ b/Classes/Service/CantoOAuthClient.php @@ -20,8 +20,12 @@ use Flownative\OAuth2\Client\OAuthClientException; use League\OAuth2\Client\Provider\GenericProvider; use Neos\Flow\Annotations as Flow; +use Neos\Flow\Http\Exception; use Neos\Flow\Http\Helper\UriHelper; +use Neos\Flow\Http\RequestHandler; use Neos\Flow\Mvc\ActionRequest; +use Neos\Flow\Mvc\Routing\Exception\MissingActionNameException; +use Neos\Flow\Persistence\Exception\IllegalObjectTypeException; use Neos\Flow\Persistence\PersistenceManagerInterface; use Neos\Flow\Security\Context; use Psr\Http\Message\UriInterface; @@ -86,6 +90,10 @@ public function getClientId(): string throw new \RuntimeException('not implemented'); } + /** + * @throws Exception + * @throws MissingActionNameException + */ protected function createOAuthProvider(string $clientId, string $clientSecret): GenericProvider { return new CantoOAuthProvider([ @@ -100,6 +108,10 @@ protected function createOAuthProvider(string $clientId, string $clientSecret): ]); } + /** + * @throws Exception + * @throws MissingActionNameException + */ public function renderFinishAuthorizationUri(): string { if (FLOW_SAPITYPE === 'CLI') { @@ -122,6 +134,11 @@ public function renderFinishAuthorizationUri(): string ); } + /** + * @throws OAuthClientException + * @throws IllegalObjectTypeException + * @throws \JsonException + */ public function finishAuthorization(string $stateIdentifier, string $code, string $scope): UriInterface { $stateFromCache = $this->stateCache->get($stateIdentifier); From 62c15fe1d90efdf19cbcab1058c82486a94f0c0d Mon Sep 17 00:00:00 2001 From: Karsten Dambekalns Date: Wed, 7 Sep 2022 18:03:52 +0200 Subject: [PATCH 4/5] Cache results of countByTag Thanks to https://github.com/danielkestler for inspiration! --- .../AssetSource/CantoAssetProxyRepository.php | 20 ++++++++++++++++--- Configuration/Caches.yaml | 6 ++++++ Configuration/Objects.yaml | 10 ++++++++++ 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/Classes/AssetSource/CantoAssetProxyRepository.php b/Classes/AssetSource/CantoAssetProxyRepository.php index e73e86c..db6d7ca 100644 --- a/Classes/AssetSource/CantoAssetProxyRepository.php +++ b/Classes/AssetSource/CantoAssetProxyRepository.php @@ -22,6 +22,7 @@ use League\OAuth2\Client\Provider\Exception\IdentityProviderException; use Neos\Cache\Exception as CacheException; use Neos\Cache\Exception\InvalidDataException; +use Neos\Cache\Frontend\VariableFrontend; use Neos\Flow\Http\Exception; use Neos\Flow\Mvc\Routing\Exception\MissingActionNameException; use Neos\Media\Domain\Model\AssetCollection; @@ -44,6 +45,11 @@ class CantoAssetProxyRepository implements AssetProxyRepositoryInterface, Suppor private string $assetTypeFilter = 'All'; private array $orderings = []; + /** + * @var VariableFrontend $apiResponsesCache + */ + protected $apiResponsesCache; + public function __construct(private CantoAssetSource $assetSource) { } @@ -185,11 +191,19 @@ public function countUntagged(): int public function countByTag(Tag $tag): int { + $identifier = 'countByTag-' . sha1($tag->getLabel()); + $cacheEntry = $this->apiResponsesCache->get($identifier); + if ($cacheEntry !== false) { + return $cacheEntry; + } + try { - return ($this->findByTag($tag))->count(); - } catch (AuthenticationFailedException|OAuthClientException|GuzzleException $e) { - return 0; + $count = $this->findByTag($tag)->count(); + $this->apiResponsesCache->set($identifier, $count, ['countByTag']); + return $count; + } catch (AuthenticationFailedException|OAuthClientException|GuzzleException) { } + return 0; } public function filterByCollection(AssetCollection $assetCollection = null): void diff --git a/Configuration/Caches.yaml b/Configuration/Caches.yaml index 687cabe..328b04d 100644 --- a/Configuration/Caches.yaml +++ b/Configuration/Caches.yaml @@ -3,3 +3,9 @@ Flownative_Canto_AssetProxy: backend: Neos\Cache\Backend\FileBackend backendOptions: defaultLifetime: 0 + +Flownative_Canto_ApiResponses: + frontend: Neos\Cache\Frontend\VariableFrontend + backend: Neos\Cache\Backend\FileBackend + backendOptions: + defaultLifetime: 3600 diff --git a/Configuration/Objects.yaml b/Configuration/Objects.yaml index 9c6cd6f..7659f1f 100644 --- a/Configuration/Objects.yaml +++ b/Configuration/Objects.yaml @@ -8,6 +8,16 @@ Flownative\Canto\AssetSource\CantoAssetSource: 1: value: Flownative_Canto_AssetProxy +Flownative\Canto\AssetSource\CantoAssetProxyRepository: + properties: + apiResponsesCache: + object: + factoryObjectName: Neos\Flow\Cache\CacheManager + factoryMethodName: getCache + arguments: + 1: + value: Flownative_Canto_ApiResponses + Flownative\Canto\Service\CantoOAuthClient: properties: stateCache: From d235084e76fa3a1e357d5de9f64669ea395b16cb Mon Sep 17 00:00:00 2001 From: Karsten Dambekalns Date: Wed, 7 Sep 2022 18:05:41 +0200 Subject: [PATCH 5/5] Cache results of getCustomFields --- Classes/Service/CantoClient.php | 13 +++++++++++-- Configuration/Objects.yaml | 10 ++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/Classes/Service/CantoClient.php b/Classes/Service/CantoClient.php index 0c7fb67..cc1213d 100644 --- a/Classes/Service/CantoClient.php +++ b/Classes/Service/CantoClient.php @@ -26,6 +26,8 @@ use GuzzleHttp\Psr7\ServerRequest; use GuzzleHttp\Psr7\Uri; use League\OAuth2\Client\Provider\Exception\IdentityProviderException; +use Neos\Cache\Exception; +use Neos\Cache\Frontend\VariableFrontend; use Neos\Flow\Annotations as Flow; use Neos\Flow\Core\Bootstrap; use Neos\Flow\Http\Exception as HttpException; @@ -215,13 +217,20 @@ public function search(string $keyword, array $formatTypes, string $customQueryP * @throws MissingClientSecretException * @throws OAuthClientException * @throws \JsonException - * @todo perhaps cache the result + * @throws Exception */ public function getCustomFields(): array { + $cacheEntry = $this->apiResponsesCache->get('customFields'); + if ($cacheEntry !== false) { + return $cacheEntry; + } + $response = $this->sendAuthenticatedRequest('custom/field'); if ($response->getStatusCode() === 200) { - return json_decode($response->getBody()->getContents(), false, 512, JSON_THROW_ON_ERROR); + $customFields = json_decode($response->getBody()->getContents(), false, 512, JSON_THROW_ON_ERROR); + $this->apiResponsesCache->set('customFields', $customFields); + return $customFields; } return []; } diff --git a/Configuration/Objects.yaml b/Configuration/Objects.yaml index 7659f1f..e6ec5c6 100644 --- a/Configuration/Objects.yaml +++ b/Configuration/Objects.yaml @@ -18,6 +18,16 @@ Flownative\Canto\AssetSource\CantoAssetProxyRepository: 1: value: Flownative_Canto_ApiResponses +Flownative\Canto\Service\CantoClient: + properties: + apiResponsesCache: + object: + factoryObjectName: Neos\Flow\Cache\CacheManager + factoryMethodName: getCache + arguments: + 1: + value: Flownative_Canto_ApiResponses + Flownative\Canto\Service\CantoOAuthClient: properties: stateCache: