Skip to content
Snippets Groups Projects
Commit fa743c09 authored by Tobias Gross-Vogt's avatar Tobias Gross-Vogt
Browse files

local data mapping: allow list of source attributes ordered by preferred usage

parent 93718322
No related branches found
No related tags found
No related merge requests found
Pipeline #227970 passed
......@@ -21,13 +21,13 @@ use Symfony\Component\HttpFoundation\Response;
abstract class AbstractLocalDataPostEventSubscriber extends AbstractAuthorizationService implements EventSubscriberInterface
{
protected const ROOT_CONFIG_NODE = 'local_data_mapping';
protected const SOURCE_ATTRIBUTE_CONFIG_NODE = 'source_attribute';
protected const SOURCE_ATTRIBUTES_CONFIG_NODE = 'source_attributes';
protected const LOCAL_DATA_ATTRIBUTE_CONFIG_NODE = 'local_data_attribute';
protected const AUTHORIZATION_EXPRESSION_CONFIG_NODE = 'authorization_expression';
protected const DEFAULT_VALUE_ATTRIBUTE_CONFIG_NODE = 'default_value';
protected const DEFAULT_VALUES_ATTRIBUTE_CONFIG_NODE = 'default_values';
private const SOURCE_ATTRIBUTE_KEY = 'source';
private const SOURCE_ATTRIBUTES_KEY = 'source';
private const DEFAULT_VALUE_KEY = 'default';
/*
......@@ -57,10 +57,16 @@ abstract class AbstractLocalDataPostEventSubscriber extends AbstractAuthorizatio
}
$attributeMapEntry = [];
$attributeMapEntry[self::SOURCE_ATTRIBUTE_KEY] = $configMappingEntry[self::SOURCE_ATTRIBUTE_CONFIG_NODE];
$attributeMapEntry[self::SOURCE_ATTRIBUTES_KEY] = $configMappingEntry[self::SOURCE_ATTRIBUTES_CONFIG_NODE];
$defaultValue = $configMappingEntry[self::DEFAULT_VALUE_ATTRIBUTE_CONFIG_NODE] ?? null;
if ($defaultValue === null) {
$defaultArray = $configMappingEntry[self::DEFAULT_VALUES_ATTRIBUTE_CONFIG_NODE] ?? null;
if ($defaultArray !== null && $defaultArray !== self::ARRAY_VALUE_NOT_SPECIFIED) {
$defaultValue = $defaultArray;
}
}
$defaultValue = $configMappingEntry[self::DEFAULT_VALUE_ATTRIBUTE_CONFIG_NODE] ??
((($defaultArray = $configMappingEntry[self::DEFAULT_VALUES_ATTRIBUTE_CONFIG_NODE]) !== self::ARRAY_VALUE_NOT_SPECIFIED) ? $defaultArray : null);
if ($defaultValue !== null) {
$attributeMapEntry[self::DEFAULT_VALUE_KEY] = $defaultValue;
}
......@@ -97,12 +103,19 @@ abstract class AbstractLocalDataPostEventSubscriber extends AbstractAuthorizatio
throw ApiError::withDetails(Response::HTTP_UNAUTHORIZED, sprintf('access to local data attribute \'%s\' denied', $localDataAttributeName));
}
$sourceAttributeName = $attributeMapEntry[self::SOURCE_ATTRIBUTE_KEY];
$attributeValue = $sourceData[$sourceAttributeName] ?? $attributeMapEntry[self::DEFAULT_VALUE_KEY] ?? null;
$attributeValue = null;
foreach ($attributeMapEntry[self::SOURCE_ATTRIBUTES_KEY] as $sourceAttributeName) {
if (($value = $sourceData[$sourceAttributeName] ?? null) !== null) {
$attributeValue = $value;
break;
}
}
$attributeValue = $attributeValue ?? $attributeMapEntry[self::DEFAULT_VALUE_KEY] ?? null;
if ($attributeValue !== null) {
$postEvent->setLocalDataAttribute($localDataAttributeName, $attributeValue);
} else {
throw ApiError::withDetails(Response::HTTP_INTERNAL_SERVER_ERROR, sprintf('attribute \'%s\' not available in source data', $sourceAttributeName));
throw ApiError::withDetails(Response::HTTP_INTERNAL_SERVER_ERROR, sprintf('none of the source attributes available for local data attribute \'%s\'', $localDataAttributeName));
}
}
}
......@@ -123,17 +136,23 @@ abstract class AbstractLocalDataPostEventSubscriber extends AbstractAuthorizatio
return $treeBuilder->getRootNode()
->arrayPrototype()
->children()
->scalarNode(self::SOURCE_ATTRIBUTE_CONFIG_NODE)->end()
->scalarNode(self::LOCAL_DATA_ATTRIBUTE_CONFIG_NODE)->end()
->scalarNode(self::LOCAL_DATA_ATTRIBUTE_CONFIG_NODE)
->info('The name of the local data attribute.')
->end()
->arrayNode(self::SOURCE_ATTRIBUTES_CONFIG_NODE)
->info('The list of source attributes to map to the local data attribute ordered by preferred usage. If an attribute is not found, the next attribute in the list is used.')
->scalarPrototype()->end()
->end()
->scalarNode(self::AUTHORIZATION_EXPRESSION_CONFIG_NODE)
->defaultValue('false')
->info('A boolean expression evaluable by the Symfony Expression Language determining whether the current user may request read the local data attribute.')
->end()
->scalarNode(self::DEFAULT_VALUE_ATTRIBUTE_CONFIG_NODE)
->info('The default value for scalar (non-array) attributes. If none is specified, an exception is thrown in the case the source attribute is not found.')
->info('The default value for scalar (i.e. non-array) attributes. If none is specified, an exception is thrown in case none of the source attributes is found.')
->end()
->arrayNode(self::DEFAULT_VALUES_ATTRIBUTE_CONFIG_NODE)
->defaultValue(self::ARRAY_VALUE_NOT_SPECIFIED)
->info('The default value for array type attributes. If none is specified, an exception is thrown in the case the source attribute is not found.')
->info('The default value for array type attributes. If none is specified, an exception is thrown in case none of the source attributes is found.')
->scalarPrototype()->end()
->end()
->end()
......
......@@ -30,14 +30,15 @@ class LocalDataEventDispatcher
private $eventDispatcher;
/**
* @param string $resourceClass The class name of the entity (resource) this event dispatcher is responsible for
* @param EventDispatcherInterface $eventDispatcher The inner event dispatcher that this event dispatcher decorates
* @param string $resourceClass The class name of the entity (resource) this event dispatcher is responsible for
* @param EventDispatcherInterface $eventDispatcher The inner event dispatcher that this event dispatcher decorates
* @param string|null $uniqueEntityName The unique name of the entity. If not specified or empty, the 'shortName' attribute of the entities @ApiResource annotation is used.
*/
public function __construct(string $resourceClass, EventDispatcherInterface $eventDispatcher)
public function __construct(string $resourceClass, EventDispatcherInterface $eventDispatcher, string $uniqueEntityName = null)
{
$this->queryParameters = [];
$this->requestedAttributes = [];
$this->uniqueEntityName = self::getUniqueEntityName($resourceClass);
$this->uniqueEntityName = !Tools::isNullOrEmpty($uniqueEntityName) ? $uniqueEntityName : self::getUniqueEntityName($resourceClass);
$this->eventDispatcher = $eventDispatcher;
}
......@@ -113,7 +114,7 @@ class LocalDataEventDispatcher
}
$uniqueName = $resourceMetadata->getShortName() ?? '';
if (empty($uniqueName)) {
if (Tools::isNullOrEmpty($uniqueName)) {
throw new ApiError(500, sprintf("'shortName' attribute missing in ApiResource annotation of resource class '%s'", $resourceClass));
} elseif (str_contains($uniqueName, '.') || str_contains($uniqueName, ',')) {
throw new ApiError(500, sprintf("'shortName' attribute of resource class '%s' must not contain '.' or ',' characters: '%s'", $resourceClass, $uniqueName));
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment