Skip to content

Commit

Permalink
IBX-7275: Exposed base admin UI configuration in REST API (#1027)
Browse files Browse the repository at this point in the history
* Added ApplicationConfig REST Value

* Added ApplicationConfigController

* Added ApplicationConfigRestResolver

* Added ApplicationConfigVisitor

* [PHPStan] Regenerated baseline

* Fixed Resolvers service definition

* Fixed phpdoc for ApplicationConfig::getConfig

* Fixes after review

* Fixed variables name

* Reworked generating custom output
  • Loading branch information
ciastektk committed Dec 11, 2023
1 parent 108fbf6 commit 3cb72e5
Show file tree
Hide file tree
Showing 12 changed files with 406 additions and 0 deletions.
28 changes: 28 additions & 0 deletions src/bundle/Controller/ApplicationConfigController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

/**
* @copyright Copyright (C) Ibexa AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
declare(strict_types=1);

namespace Ibexa\Bundle\AdminUi\Controller;

use Ibexa\AdminUi\REST\Value\ApplicationConfig;
use Ibexa\AdminUi\UI\Config\Aggregator;
use Ibexa\Rest\Server\Controller;

final class ApplicationConfigController extends Controller
{
private Aggregator $aggregator;

public function __construct(Aggregator $aggregator)
{
$this->aggregator = $aggregator;
}

public function loadConfigAction(): ApplicationConfig
{
return new ApplicationConfig($this->aggregator->getConfig());
}
}
11 changes: 11 additions & 0 deletions src/bundle/Resources/config/routing_rest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,14 @@ ibexa.udw.accordion.gridview.data:
methods: [GET]
options:
expose: true

#
# ApplicationConfig
#

ibexa.rest.application_config:
path: /application-config
controller: 'Ibexa\Bundle\AdminUi\Controller\ApplicationConfigController::loadConfigAction'
methods: [GET]
options:
expose: true
5 changes: 5 additions & 0 deletions src/bundle/Resources/config/services/controllers.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -222,5 +222,10 @@ services:
Ibexa\Bundle\AdminUi\Controller\Permission\LanguageLimitationController:
parent: Ibexa\Contracts\AdminUi\Controller\Controller
autowire: true


Ibexa\Bundle\AdminUi\Controller\ApplicationConfigController:
parent: Ibexa\Rest\Server\Controller
autowire: true
tags:
- controller.service_arguments
10 changes: 10 additions & 0 deletions src/bundle/Resources/config/services/rest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,13 @@ services:
parent: Ibexa\Contracts\Rest\Output\ValueObjectVisitor
tags:
- { name: ibexa.rest.output.value_object.visitor, type: Ibexa\AdminUi\REST\Value\UniversalDiscovery\AccordionData }

#
# ApplicationConfig
#
Ibexa\AdminUi\REST\Output\ValueObjectVisitor\ApplicationConfigVisitor:
parent: Ibexa\Contracts\Rest\Output\ValueObjectVisitor
arguments:
$applicationConfigRestGeneratorRegistry: '@Ibexa\Contracts\AdminUi\REST\ApplicationConfigRestGeneratorRegistryInterface'
tags:
- { name: ibexa.rest.output.value_object.visitor, type: Ibexa\AdminUi\REST\Value\ApplicationConfig }
16 changes: 16 additions & 0 deletions src/bundle/Resources/config/services/ui_config/common.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -134,3 +134,19 @@ services:
$resultLimit: '%ibexa.site_access.config.default.search.suggestion.result_limit%'
tags:
- { name: ibexa.admin_ui.config.provider, key: 'suggestions' }

# Resolvers
Ibexa\AdminUi\REST\Generator\ApplicationConfigRestGeneratorRegistry:
arguments:
$generators: !tagged_iterator { tag: ibexa.admin_ui.config.provider.rest.generator }

Ibexa\Contracts\AdminUi\REST\ApplicationConfigRestGeneratorRegistryInterface:
alias: Ibexa\AdminUi\REST\Generator\ApplicationConfigRestGeneratorRegistry

Ibexa\AdminUi\REST\Generator\UserConfigRestGenerator:
tags:
- { name: ibexa.admin_ui.config.provider.rest.generator, priority: -10 }

Ibexa\AdminUi\REST\Generator\ProfilePictureFieldConfigRestGenerator:
tags:
- { name: ibexa.admin_ui.config.provider.rest.generator, priority: -10 }
24 changes: 24 additions & 0 deletions src/contracts/REST/ApplicationConfigRestGeneratorInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

/**
* @copyright Copyright (C) Ibexa AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
declare(strict_types=1);

namespace Ibexa\Contracts\AdminUi\REST;

use Ibexa\Contracts\Rest\Output\Generator;
use Ibexa\Contracts\Rest\Output\Visitor;

interface ApplicationConfigRestGeneratorInterface
{
public function supportsNamespace(string $namespace): bool;

public function supportsParameter(string $parameterName): bool;

/**
* @param mixed $parameter
*/
public function generate($parameter, Generator $generator, Visitor $visitor): void;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

/**
* @copyright Copyright (C) Ibexa AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
declare(strict_types=1);

namespace Ibexa\Contracts\AdminUi\REST;

interface ApplicationConfigRestGeneratorRegistryInterface
{
public function hasGenerator(string $namespace, string $parameter): bool;

public function hasGenerators(string $namespace): bool;

public function getGenerator(string $namespace, string $parameter): ApplicationConfigRestGeneratorInterface;

/**
* @return iterable<\Ibexa\Contracts\AdminUi\REST\ApplicationConfigRestGeneratorInterface>
*/
public function getGenerators(string $namespace): iterable;
}
90 changes: 90 additions & 0 deletions src/lib/REST/Generator/ApplicationConfigRestGeneratorRegistry.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
<?php

/**
* @copyright Copyright (C) Ibexa AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
declare(strict_types=1);

namespace Ibexa\AdminUi\REST\Generator;

use Ibexa\Contracts\AdminUi\REST\ApplicationConfigRestGeneratorInterface;
use Ibexa\Contracts\AdminUi\REST\ApplicationConfigRestGeneratorRegistryInterface;
use RuntimeException;

final class ApplicationConfigRestGeneratorRegistry implements ApplicationConfigRestGeneratorRegistryInterface
{
/** @var iterable<\Ibexa\Contracts\AdminUi\REST\ApplicationConfigRestGeneratorInterface> */
private iterable $generators;

/**
* @param iterable<\Ibexa\Contracts\AdminUi\REST\ApplicationConfigRestGeneratorInterface> $generators
*/
public function __construct(iterable $generators)
{
$this->generators = $generators;
}

public function hasGenerator(
string $namespace,
string $parameter
): bool {
foreach ($this->generators as $generator) {
if (
$generator->supportsNamespace($namespace)
&& $generator->supportsParameter($parameter)
) {
return true;
}
}

return false;
}

public function hasGenerators(string $namespace): bool
{
foreach ($this->generators as $generator) {
if ($generator->supportsNamespace($namespace)) {
return true;
}
}

return false;
}

public function getGenerator(string $namespace, string $parameter): ApplicationConfigRestGeneratorInterface
{
foreach ($this->generators as $generator) {
if (
$generator->supportsNamespace($namespace)
&& $generator->supportsParameter($parameter)
) {
return $generator;
}
}

throw new RuntimeException(
sprintf(
'No Generator found for Configuration in namespace \'%s\' and parameter \'%s\'',
$namespace,
$parameter
)
);
}

/**
* @return iterable<\Ibexa\Contracts\AdminUi\REST\ApplicationConfigRestGeneratorInterface>
*/
public function getGenerators(string $namespace): iterable
{
foreach ($this->generators as $generator) {
if ($generator->supportsNamespace($namespace)) {
yield $generator;
}
}

throw new RuntimeException(
'No Generators found for Configuration in namespace \'' . $namespace . '\'',
);
}
}
43 changes: 43 additions & 0 deletions src/lib/REST/Generator/ProfilePictureFieldConfigRestGenerator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

/**
* @copyright Copyright (C) Ibexa AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
declare(strict_types=1);

namespace Ibexa\AdminUi\REST\Generator;

use Ibexa\Contracts\AdminUi\REST\ApplicationConfigRestGeneratorInterface;
use Ibexa\Contracts\Core\Repository\Values\Content\Field;
use Ibexa\Contracts\Rest\Output\Generator;
use Ibexa\Contracts\Rest\Output\Visitor;

final class ProfilePictureFieldConfigRestGenerator implements ApplicationConfigRestGeneratorInterface
{
private const NAMESPACE = 'user';
private const PARAMETER = 'profile_picture_field';

public function supportsNamespace(string $namespace): bool
{
return self::NAMESPACE === $namespace;
}

public function supportsParameter(string $parameterName): bool
{
return self::PARAMETER === $parameterName;
}

public function generate($parameter, Generator $generator, Visitor $visitor): void
{
if ($parameter instanceof Field) {
$generator->startHashElement(self::PARAMETER);
$visitor->visitValueObject($parameter);
$generator->endHashElement(self::PARAMETER);

return;
}

$generator->generateFieldTypeHash(self::PARAMETER, $parameter);
}
}
43 changes: 43 additions & 0 deletions src/lib/REST/Generator/UserConfigRestGenerator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

/**
* @copyright Copyright (C) Ibexa AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
declare(strict_types=1);

namespace Ibexa\AdminUi\REST\Generator;

use Ibexa\Contracts\AdminUi\REST\ApplicationConfigRestGeneratorInterface;
use Ibexa\Contracts\Core\Repository\Values\User\User;
use Ibexa\Contracts\Rest\Output\Generator;
use Ibexa\Contracts\Rest\Output\Visitor;

final class UserConfigRestGenerator implements ApplicationConfigRestGeneratorInterface
{
private const NAMESPACE = 'user';
private const PARAMETER = 'user';

public function supportsNamespace(string $namespace): bool
{
return self::NAMESPACE === $namespace;
}

public function supportsParameter(string $parameterName): bool
{
return self::PARAMETER === $parameterName;
}

public function generate($parameter, Generator $generator, Visitor $visitor): void
{
if ($parameter instanceof User) {
$generator->startHashElement(self::PARAMETER);
$visitor->visitValueObject($parameter);
$generator->endHashElement(self::PARAMETER);

return;
}

$generator->generateFieldTypeHash(self::PARAMETER, $parameter);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<?php

/**
* @copyright Copyright (C) Ibexa AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
declare(strict_types=1);

namespace Ibexa\AdminUi\REST\Output\ValueObjectVisitor;

use Ibexa\Contracts\AdminUi\REST\ApplicationConfigRestGeneratorRegistryInterface;
use Ibexa\Contracts\Rest\Output\Generator;
use Ibexa\Contracts\Rest\Output\ValueObjectVisitor;
use Ibexa\Contracts\Rest\Output\Visitor;

/**
* @internal
*/
final class ApplicationConfigVisitor extends ValueObjectVisitor
{
private ApplicationConfigRestGeneratorRegistryInterface $applicationConfigRestGeneratorRegistry;

public function __construct(ApplicationConfigRestGeneratorRegistryInterface $applicationConfigRestGeneratorRegistry)
{
$this->applicationConfigRestGeneratorRegistry = $applicationConfigRestGeneratorRegistry;
}

/**
* @param \Ibexa\AdminUi\REST\Value\ApplicationConfig $data
*/
public function visit(Visitor $visitor, Generator $generator, $data): void
{
$generator->startObjectElement('ApplicationConfig');
$visitor->setHeader('Content-Type', $generator->getMediaType('ApplicationConfig'));

foreach ($data->getConfig() as $namespace => $config) {
// Checks if namespace has internal generators to generate custom output.
if ($this->applicationConfigRestGeneratorRegistry->hasGenerators($namespace)) {
$this->visitInternalGenerator(
$visitor,
$generator,
$namespace,
$config
);

continue;
}

$generator->generateFieldTypeHash($namespace, $config);
}

$generator->endObjectElement('ApplicationConfig');
}

/**
* @param array<string, mixed> $config
*/
private function visitInternalGenerator(
Visitor $visitor,
Generator $generator,
string $namespace,
array $config
): void {
$generator->startHashElement($namespace);

foreach ($config as $name => $value) {
if (!$this->applicationConfigRestGeneratorRegistry->hasGenerator($namespace, $name)) {
$generator->generateFieldTypeHash($name, $value);

continue;
}

$this->applicationConfigRestGeneratorRegistry
->getGenerator($namespace, $name)
->generate($value, $generator, $visitor);
}

$generator->endHashElement($namespace);
}
}
Loading

0 comments on commit 3cb72e5

Please sign in to comment.