Skip to content

Commit 208681b

Browse files
authored
[Studio] Config update endpoint (#1020)
* Added update endpoint * Adapted schema for update endpoint * Service cleanup
1 parent 8479add commit 208681b

9 files changed

Lines changed: 451 additions & 95 deletions

File tree

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
/**
5+
* This source file is available under the terms of the
6+
* Pimcore Open Core License (POCL)
7+
* Full copyright and license information is available in
8+
* LICENSE.md which is distributed with this source code.
9+
*
10+
* @copyright Copyright (c) Pimcore GmbH (https://www.pimcore.com)
11+
* @license Pimcore Open Core License (POCL)
12+
*/
13+
14+
namespace Pimcore\Bundle\DataHubBundle\Controller\Studio\Config;
15+
16+
use Exception;
17+
use OpenApi\Attributes\JsonContent;
18+
use OpenApi\Attributes\Put;
19+
use OpenApi\Attributes\RequestBody;
20+
use OpenApi\Attributes\Schema;
21+
use Pimcore\Bundle\DataHubBundle\OpenApi\Config\Prefix;
22+
use Pimcore\Bundle\DataHubBundle\OpenApi\Config\Tags;
23+
use Pimcore\Bundle\DataHubBundle\Schema\UpdateConfiguration;
24+
use Pimcore\Bundle\DataHubBundle\Schema\UpdateConfigurationResponse;
25+
use Pimcore\Bundle\DataHubBundle\Service\Studio\ConfigurationServiceInterface;
26+
use Pimcore\Bundle\DataHubBundle\Utils\Constants\PermissionConstants;
27+
use Pimcore\Bundle\StudioBackendBundle\Controller\AbstractApiController;
28+
use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Parameter\Path\IdParameter;
29+
use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Response\DefaultResponses;
30+
use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Response\SuccessResponse;
31+
use Pimcore\Bundle\StudioBackendBundle\Util\Constant\HttpResponseCodes;
32+
use Symfony\Component\HttpFoundation\JsonResponse;
33+
use Symfony\Component\HttpKernel\Attribute\MapRequestPayload;
34+
use Symfony\Component\Routing\Attribute\Route;
35+
use Symfony\Component\Security\Http\Attribute\IsGranted;
36+
use Symfony\Component\Serializer\SerializerInterface;
37+
38+
/**
39+
* @internal
40+
*/
41+
final class UpdateController extends AbstractApiController
42+
{
43+
private const string ROUTE = '/config/{name}';
44+
45+
public function __construct(
46+
SerializerInterface $serializer,
47+
private readonly ConfigurationServiceInterface $configurationService
48+
) {
49+
parent::__construct($serializer);
50+
}
51+
52+
/**
53+
* @throws Exception
54+
*/
55+
#[Route(
56+
path: self::ROUTE,
57+
name: 'pimcore_studio_api_data_hub_config_update',
58+
methods: ['PUT']
59+
)]
60+
#[Put(
61+
path: Prefix::BUNDLE . self::ROUTE,
62+
operationId: 'bundle_data_hub_config_update',
63+
description: 'bundle_data_hub_config_update_description',
64+
summary: 'bundle_data_hub_config_update_summary',
65+
tags: [Tags::DataHub->value]
66+
)]
67+
#[IdParameter(
68+
type: 'configuration',
69+
schema: new Schema(type: 'string'),
70+
name: 'name',
71+
)]
72+
#[RequestBody(
73+
required: true,
74+
content: new JsonContent(ref: UpdateConfiguration::class)
75+
)]
76+
#[SuccessResponse(
77+
description: 'bundle_data_hub_config_update_success_response',
78+
content: new JsonContent(ref: UpdateConfigurationResponse::class)
79+
)]
80+
#[IsGranted(PermissionConstants::PLUGIN_DATA_HUB_CONFIG)]
81+
#[DefaultResponses([
82+
HttpResponseCodes::UNAUTHORIZED,
83+
HttpResponseCodes::NOT_FOUND,
84+
HttpResponseCodes::CONFLICT,
85+
])]
86+
public function updateConfiguration(
87+
string $name,
88+
#[MapRequestPayload] UpdateConfiguration $updateConfiguration
89+
): JsonResponse {
90+
$modificationDate = $this->configurationService->updateConfiguration(
91+
$name,
92+
$updateConfiguration->getConfiguration(),
93+
$updateConfiguration->getModificationDate()
94+
);
95+
96+
return $this->jsonResponse(
97+
new UpdateConfigurationResponse($modificationDate)
98+
);
99+
}
100+
}
101+
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
/**
5+
* This source file is available under the terms of the
6+
* Pimcore Open Core License (POCL)
7+
* Full copyright and license information is available in
8+
* LICENSE.md which is distributed with this source code.
9+
*
10+
* @copyright Copyright (c) Pimcore GmbH (https://www.pimcore.com)
11+
* @license Pimcore Open Core License (POCL)
12+
*/
13+
14+
namespace Pimcore\Bundle\DataHubBundle\Hydrator;
15+
16+
/**
17+
* @internal
18+
*
19+
* Dehydrates configuration data from client/API format to storage format.
20+
* This is the inverse operation of hydration - converting from DTOs/schemas
21+
* back to the internal domain model format.
22+
*/
23+
final readonly class ConfigurationDehydrator implements ConfigurationDehydratorInterface
24+
{
25+
public function dehydrate(array $configuration): array
26+
{
27+
$configuration = $this->dehydrateSchemaEntities($configuration);
28+
$configuration = $this->dehydrateSpecialEntities($configuration);
29+
return $this->dehydrateApiKeys($configuration);
30+
}
31+
32+
private function dehydrateSchemaEntities(array $configuration): array
33+
{
34+
$keys = ['queryEntities', 'mutationEntities'];
35+
foreach ($keys as $key) {
36+
$transformedEntities = [];
37+
if (isset($configuration['schema'][$key]) && is_array($configuration['schema'][$key])) {
38+
foreach ($configuration['schema'][$key] as $entity) {
39+
if (isset($entity['id'])) {
40+
$transformedEntities[$entity['id']] = $entity;
41+
}
42+
}
43+
}
44+
$configuration['schema'][$key] = $transformedEntities;
45+
}
46+
47+
return $configuration;
48+
}
49+
50+
private function dehydrateSpecialEntities(array $configuration): array
51+
{
52+
if (!isset($configuration['schema']['specialEntities']) || !is_array($configuration['schema']['specialEntities'])) {
53+
return $configuration;
54+
}
55+
56+
$transformedEntities = [];
57+
58+
foreach ($configuration['schema']['specialEntities'] as $entity) {
59+
if (isset($entity['name'])) {
60+
$transformedEntities[$entity['name']] = [
61+
'read' => $entity['readAllowed'] ?? false,
62+
'create' => $entity['createAllowed'] ?? false,
63+
'update' => $entity['updateAllowed'] ?? false,
64+
'delete' => $entity['deleteAllowed'] ?? false,
65+
];
66+
}
67+
}
68+
69+
$configuration['schema']['specialEntities'] = $transformedEntities;
70+
71+
return $configuration;
72+
}
73+
74+
private function dehydrateApiKeys(array $configuration): array
75+
{
76+
if (isset($configuration['security']['apikey']) && is_string($configuration['security']['apikey'])) {
77+
$configuration['security']['apikey'] = explode(
78+
"\n",
79+
trim($configuration['security']['apikey'], "\n")
80+
);
81+
}
82+
83+
return $configuration;
84+
}
85+
}
86+
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
/**
5+
* This source file is available under the terms of the
6+
* Pimcore Open Core License (POCL)
7+
* Full copyright and license information is available in
8+
* LICENSE.md which is distributed with this source code.
9+
*
10+
* @copyright Copyright (c) Pimcore GmbH (https://www.pimcore.com)
11+
* @license Pimcore Open Core License (POCL)
12+
*/
13+
14+
namespace Pimcore\Bundle\DataHubBundle\Hydrator;
15+
16+
/**
17+
* @internal
18+
*/
19+
interface ConfigurationDehydratorInterface
20+
{
21+
/**
22+
* Dehydrate configuration from client/API format to storage format
23+
*
24+
* Transforms:
25+
* - Query/mutation entities from arrays to associative arrays indexed by id
26+
* - Special entities from client format (readAllowed, createAllowed, etc.) to storage format (read, create, etc.)
27+
* - API keys from multiline string to array
28+
*/
29+
public function dehydrate(array $configuration): array;
30+
}
31+

src/Resources/config/studio_backend.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ services:
2424
Pimcore\Bundle\DataHubBundle\Hydrator\ConfigurationDetailHydratorInterface:
2525
class: Pimcore\Bundle\DataHubBundle\Hydrator\ConfigurationDetailHydrator
2626

27+
Pimcore\Bundle\DataHubBundle\Hydrator\ConfigurationDehydratorInterface:
28+
class: Pimcore\Bundle\DataHubBundle\Hydrator\ConfigurationDehydrator
29+
2730
Pimcore\Bundle\DataHubBundle\Hydrator\PermissionUserHydratorInterface:
2831
class: Pimcore\Bundle\DataHubBundle\Hydrator\PermissionUserHydrator
2932

src/Resources/translations/studio_api_docs.en.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ bundle_data_hub_config_get: Get Data Hub Configuration
1818
bundle_data_hub_config_get_description: Get a Data Hub configuration by name
1919
bundle_data_hub_config_get_summary: Get Data Hub Configuration
2020
bundle_data_hub_config_get_success_response: Data Hub configuration details
21+
pimcore_studio_api_data_hub_config_update: Update Data Hub Configuration
22+
bundle_data_hub_config_update: Update Data Hub Configuration
23+
bundle_data_hub_config_update_description: Update an existing Data Hub configuration with validation for concurrent modifications
24+
bundle_data_hub_config_update_summary: Update Data Hub Configuration
25+
bundle_data_hub_config_update_success_response: Data Hub configuration successfully updated
2126
pimcore_studio_api_data_hub_config_clone: Clone Data Hub Configuration
2227
bundle_data_hub_config_clone: Clone Data Hub Configuration
2328
bundle_data_hub_config_clone_description: Clone an existing Data Hub configuration with a new name

src/Schema/UpdateConfiguration.php

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
/**
5+
* This source file is available under the terms of the
6+
* Pimcore Open Core License (POCL)
7+
* Full copyright and license information is available in
8+
* LICENSE.md which is distributed with this source code.
9+
*
10+
* @copyright Copyright (c) Pimcore GmbH (https://www.pimcore.com)
11+
* @license Pimcore Open Core License (POCL)
12+
*/
13+
14+
namespace Pimcore\Bundle\DataHubBundle\Schema;
15+
16+
use JsonException;
17+
use OpenApi\Attributes\Property;
18+
use OpenApi\Attributes\Schema;
19+
20+
/**
21+
* @internal
22+
*/
23+
#[Schema(
24+
schema: 'BundleDataHubUpdateConfiguration',
25+
title: 'Bundle Data Hub Update Configuration',
26+
required: ['data', 'modificationDate'],
27+
type: 'object'
28+
)]
29+
final readonly class UpdateConfiguration
30+
{
31+
public function __construct(
32+
#[Property(
33+
description: 'Configuration data as JSON string containing general settings, schema (queryEntities, mutationEntities, specialEntities), security, workspaces, and permissions',
34+
type: 'string',
35+
example: '{"general":{"active":true,"type":"GraphQL","name":"assets","description":"","group":"GQL"},"schema":{"queryEntities":[],"mutationEntities":[],"specialEntities":[]},"security":{"method":"datahub_apikey","apikey":"your-key","skipPermissionCheck":false,"disableIntrospection":false},"workspaces":{"asset":[],"document":[],"object":[]},"permissions":{"user":[],"role":[]}}'
36+
)]
37+
private string $data,
38+
#[Property(
39+
description: 'Client-side modification date timestamp for conflict detection',
40+
type: 'integer',
41+
example: 1768215191
42+
)]
43+
private int $modificationDate,
44+
) {
45+
}
46+
47+
public function getData(): string
48+
{
49+
return $this->data;
50+
}
51+
52+
/**
53+
* @throws JsonException
54+
*/
55+
public function getConfiguration(): array
56+
{
57+
return json_decode(
58+
$this->data,
59+
true,
60+
512,
61+
JSON_THROW_ON_ERROR
62+
);
63+
}
64+
65+
public function getModificationDate(): int
66+
{
67+
return $this->modificationDate;
68+
}
69+
}
70+
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
/**
5+
* This source file is available under the terms of the
6+
* Pimcore Open Core License (POCL)
7+
* Full copyright and license information is available in
8+
* LICENSE.md which is distributed with this source code.
9+
*
10+
* @copyright Copyright (c) Pimcore GmbH (https://www.pimcore.com)
11+
* @license Pimcore Open Core License (POCL)
12+
*/
13+
14+
namespace Pimcore\Bundle\DataHubBundle\Schema;
15+
16+
use OpenApi\Attributes\Property;
17+
use OpenApi\Attributes\Schema;
18+
19+
/**
20+
* @internal
21+
*/
22+
#[Schema(
23+
schema: 'BundleDataHubUpdateConfigurationResponse',
24+
title: 'Bundle Data Hub Update Configuration Response',
25+
required: ['modificationDate'],
26+
type: 'object'
27+
)]
28+
final readonly class UpdateConfigurationResponse
29+
{
30+
public function __construct(
31+
#[Property(description: 'New modification date timestamp', type: 'integer', example: 1705075200)]
32+
private int $modificationDate,
33+
) {
34+
}
35+
36+
public function getModificationDate(): int
37+
{
38+
return $this->modificationDate;
39+
}
40+
}
41+

0 commit comments

Comments
 (0)