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

FEATURE: Implement Media Details Screen #148

Merged
merged 9 commits into from
Jan 12, 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
46 changes: 44 additions & 2 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,48 @@ jobs:
php_version: 7.4
command: analyse

php-unit-tests:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: 7.4

- name: Cache dependencies
uses: actions/cache@v2
with:
path: ~/.composer/cache
key: dependencies-composer-${{ hashFiles('composer.json') }}

- name: Install dependencies
uses: php-actions/composer@v6
with:
php_version: 7.4
version: 2

- name: Run PHPUnit tests
run: composer test

js-unit-tests:
runs-on: ubuntu-latest

steps:
- uses: actions/setup-node@v1
with:
node-version: '16'
cache: 'yarn'
- uses: actions/checkout@v2

- name: Install dependencies
run: yarn

- name: Run mocha tests
run: yarn test:unit

e2e:
env:
# Solves error when parcel tries to count cpus via lscpu
Expand All @@ -72,8 +114,8 @@ jobs:
- name: Build main module
run: yarn build:module

- name: Build secondary editor for Neos UI
run: yarn build:editor
- name: Build plugin for Neos UI
run: yarn build:plugin

- name: Run TestCafe
run: yarn test:github-actions
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
node_modules
.yarn/cache
.yarn/install-state.gz
Packages
vendor
composer.lock

#
# Compiled assets.
Expand Down
31 changes: 31 additions & 0 deletions Classes/Domain/Model/AssetProxyIteratorAggregate.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

declare(strict_types=1);

namespace Flowpack\Media\Ui\Domain\Model;

/*
* This file is part of the Flowpack.Media.Ui package.
*
* (c) Contributors of the Neos Project - www.neos.io
*
* This package is Open Source Software. For the full copyright and license
* information, please view the LICENSE file which was distributed with this
* source code.
*/

use Neos\Media\Domain\Model\AssetSource\AssetProxy\AssetProxyInterface;

/**
* @extends \IteratorAggregate<AssetProxyInterface>
*/
interface AssetProxyIteratorAggregate extends \Countable, \IteratorAggregate
{
public function setOffset(int $offset): void;

/**
* @param null|integer $limit
* @return void
*/
public function setLimit($limit): void;
}
67 changes: 67 additions & 0 deletions Classes/Domain/Model/SearchTerm.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<?php

declare(strict_types=1);

namespace Flowpack\Media\Ui\Domain\Model;

/*
* This file is part of the Flowpack.Media.Ui package.
*
* (c) Contributors of the Neos Project - www.neos.io
*
* This package is Open Source Software. For the full copyright and license
* information, please view the LICENSE file which was distributed with this
* source code.
*/

/**
* @internal
*/
final class SearchTerm
{
const ASSET_IDENTIFIER_PATTERN = '/id:([0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12})/';

/**
* @var string
*/
private $value;

/**
* @var null|string
*/
private $assetIdentifier = null;

private function __construct(string $value)
{
$this->value = $value;

if (preg_match(self::ASSET_IDENTIFIER_PATTERN, $value, $matches) !== false) {
if ($assetIdentifier = $matches[1] ?? null) {
$this->assetIdentifier = $assetIdentifier;
}
}
}

/**
* @param mixed $any
* @return null|self
*/
public static function from($any): ?self
{
if (is_string($any) && !empty($any)) {
return new self($any);
}

return null;
}

public function getAssetIdentifierIfPresent(): ?string
{
return $this->assetIdentifier;
}

public function __toString(): string
{
return $this->value;
}
}
63 changes: 33 additions & 30 deletions Classes/GraphQL/Resolver/Type/QueryResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@

use Flowpack\Media\Ui\Exception as MediaUiException;
use Flowpack\Media\Ui\Domain\ImageMapper;
use Flowpack\Media\Ui\Domain\Model\AssetProxyIteratorAggregate;
use Flowpack\Media\Ui\Domain\Model\SearchTerm;
use Flowpack\Media\Ui\GraphQL\Context\AssetSourceContext;
use Flowpack\Media\Ui\Infrastructure\Neos\Media\AssetProxyListIterator;
use Flowpack\Media\Ui\Infrastructure\Neos\Media\AssetProxyQueryIterator;
use Flowpack\Media\Ui\Service\AssetChangeLog;
use Flowpack\Media\Ui\Service\SimilarityService;
use Flowpack\Media\Ui\Service\UsageDetailsService;
Expand Down Expand Up @@ -116,32 +120,27 @@ class QueryResolver implements ResolverInterface
*/
public function assetCount($_, array $variables, AssetSourceContext $assetSourceContext): int
{
$query = $this->createAssetProxyQuery($variables, $assetSourceContext);
$iterator = $this->createAssetProxyIterator($variables, $assetSourceContext);

if (!$query) {
if (!$iterator) {
$this->systemLogger->error('Could not build asset query for given variables', $variables);
return 0;
}

try {
return $query->execute()->count();
} catch (\Exception $e) {
// TODO: Handle that not every asset source implements the count method => Introduce countable interface?
}
return 0;
return count($iterator);
}

/**
* Helper to create a asset proxy query for other methods
*
* @param array $variables
* @param AssetSourceContext $assetSourceContext
* @return AssetProxyQueryInterface|null
* @return AssetProxyIteratorAggregate|null
*/
protected function createAssetProxyQuery(
protected function createAssetProxyIterator(
array $variables,
AssetSourceContext $assetSourceContext
): ?AssetProxyQueryInterface {
): ?AssetProxyIteratorAggregate {
[
'assetSourceId' => $assetSourceId,
'tagId' => $tagId,
Expand Down Expand Up @@ -199,15 +198,27 @@ protected function createAssetProxyQuery(
/** @var Tag $tag */
$tag = $this->tagRepository->findByIdentifier($tagId);
if ($tag) {
return $assetProxyRepository->findByTag($tag)->getQuery();
return AssetProxyQueryIterator::from(
$assetProxyRepository->findByTag($tag)->getQuery()
);
}
}

if (is_string($searchTerm) && !empty($searchTerm)) {
return $assetProxyRepository->findBySearchTerm($searchTerm)->getQuery();
if ($searchTerm = SearchTerm::from($searchTerm)) {
if ($identifier = $searchTerm->getAssetIdentifierIfPresent()) {
return AssetProxyListIterator::of(
$assetProxyRepository->getAssetProxy($identifier)
);
} else {
return AssetProxyQueryIterator::from(
$assetProxyRepository->findBySearchTerm((string) $searchTerm)->getQuery()
);
}
}

return $assetProxyRepository->findAll()->getQuery();
return AssetProxyQueryIterator::from(
$assetProxyRepository->findAll()->getQuery()
);
}

/**
Expand Down Expand Up @@ -318,33 +329,25 @@ protected function getMaximumFileUploadLimit(): int
* @param $_
* @param array $variables
* @param AssetSourceContext $assetSourceContext
* @return AssetProxyQueryResultInterface|null
* @return AssetProxyIteratorAggregate|null
*/
public function assets(
$_,
array $variables,
AssetSourceContext $assetSourceContext
): ?AssetProxyQueryResultInterface {
): ?AssetProxyIteratorAggregate {
['limit' => $limit, 'offset' => $offset] = $variables + ['limit' => 20, 'offset' => 0];
$query = $this->createAssetProxyQuery($variables, $assetSourceContext);
$iterator = $this->createAssetProxyIterator($variables, $assetSourceContext);

if (!$query) {
if (!$iterator) {
$this->systemLogger->error('Could not build assets query for given variables', $variables);
return null;
}

try {
// TODO: Check if it's an issue to execute the query a second time just to get the correct number of results?
$offset = $offset < $query->execute()->count() ? $offset : 0;
} catch (\Exception $e) {
// TODO: Handle that not every asset source implements the count method => Introduce countable interface?
}

$query->setOffset($offset);
$query->setLimit($limit);
$iterator->setOffset($offset);
$iterator->setLimit($limit);

// TODO: It's not possible to use `toArray` here as not all asset sources implement it
return $query->execute();
return $iterator;
}

/**
Expand Down
80 changes: 80 additions & 0 deletions Classes/Infrastructure/Neos/Media/AssetProxyListIterator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<?php

declare(strict_types=1);

namespace Flowpack\Media\Ui\Infrastructure\Neos\Media;

/*
* This file is part of the Flowpack.Media.Ui package.
*
* (c) Contributors of the Neos Project - www.neos.io
*
* This package is Open Source Software. For the full copyright and license
* information, please view the LICENSE file which was distributed with this
* source code.
*/

use Flowpack\Media\Ui\Domain\Model\AssetProxyIteratorAggregate;
use Neos\Media\Domain\Model\AssetSource\AssetProxy\AssetProxyInterface;

/**
* @internal
*/
final class AssetProxyListIterator implements AssetProxyIteratorAggregate
{
/**
* @var AssetProxyInterface[]
*/
private $assetProxies;

/**
* @var integer
*/
private $offset = 0;

/**
* @var null|integer
*/
private $limit = null;

private function __construct(AssetProxyInterface ...$assetProxies)
{
$this->assetProxies = $assetProxies;
}

public static function of(AssetProxyInterface ...$assetProxies): self
{
return new self(...$assetProxies);
}

public function setOffset(int $offset): void
{
$this->offset = $offset;
}

/**
* @param null|integer $limit
* @return void
*/
public function setLimit($limit): void
{
// TODO: Replace this assertion with a proper type hint
// once support for PHP 7.0 is dropped
assert($limit === null || is_int($limit));

$this->limit = $limit;
}

public function count(): int
{
return count($this->assetProxies);
}

/**
* @return \Traversable<AssetProxyInterface>
*/
public function getIterator(): \Traversable
{
yield from array_slice($this->assetProxies, $this->offset, $this->limit);
}
}
Loading