Skip to content

Commit 3431b14

Browse files
committed
feat(policy): resolve group value-choice layers by union
Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
1 parent ba82508 commit 3431b14

1 file changed

Lines changed: 160 additions & 5 deletions

File tree

lib/Service/Policy/Runtime/DefaultPolicyResolver.php

Lines changed: 160 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,17 +50,30 @@ public function resolve(IPolicyDefinition $definition, PolicyContext $context):
5050
);
5151
}
5252

53-
foreach ($groupLayers as $layer) {
54-
[$currentValue, $currentSourceScope, $canOverrideBelow, $visible] = $this->applyLayer(
53+
if ($definition->resolutionMode() === 'value_choice') {
54+
[$currentValue, $currentSourceScope, $canOverrideBelow, $visible] = $this->applyValueChoiceGroupLayers(
5555
$definition,
5656
$resolved,
57-
$layer,
57+
$groupLayers,
5858
$context,
5959
$currentValue,
6060
$currentSourceScope,
6161
$canOverrideBelow,
6262
$visible,
6363
);
64+
} else {
65+
foreach ($groupLayers as $layer) {
66+
[$currentValue, $currentSourceScope, $canOverrideBelow, $visible] = $this->applyLayer(
67+
$definition,
68+
$resolved,
69+
$layer,
70+
$context,
71+
$currentValue,
72+
$currentSourceScope,
73+
$canOverrideBelow,
74+
$visible,
75+
);
76+
}
6477
}
6578

6679
$userPreference = $this->source->loadUserPreference($policyKey, $context);
@@ -99,6 +112,62 @@ public function resolve(IPolicyDefinition $definition, PolicyContext $context):
99112
return $resolved;
100113
}
101114

115+
/**
116+
* @param list<PolicyLayer> $layers
117+
* @return array{0: mixed, 1: string, 2: bool, 3: bool}
118+
*/
119+
private function applyValueChoiceGroupLayers(
120+
IPolicyDefinition $definition,
121+
ResolvedPolicy $resolved,
122+
array $layers,
123+
PolicyContext $context,
124+
mixed $currentValue,
125+
string $currentSourceScope,
126+
bool $canOverrideBelow,
127+
bool $visible,
128+
): array {
129+
if ($layers === [] || !$visible || !$canOverrideBelow) {
130+
return [$currentValue, $currentSourceScope, $canOverrideBelow, $visible];
131+
}
132+
133+
$upstreamAllowedValues = $resolved->getAllowedValues();
134+
$combinedChoices = [];
135+
$groupDefaultValues = [];
136+
$hasVisibleLayer = false;
137+
138+
foreach ($layers as $layer) {
139+
if (!$layer->isVisibleToChild()) {
140+
continue;
141+
}
142+
143+
$hasVisibleLayer = true;
144+
$layerChoices = $this->resolveValueChoiceLayerChoices($definition, $layer, $upstreamAllowedValues, $context);
145+
$combinedChoices = $this->mergeUnionAllowedValues(
146+
$definition->allowedValues($context),
147+
$combinedChoices,
148+
$layerChoices,
149+
);
150+
151+
$normalizedDefault = $definition->normalizeValue($layer->getValue());
152+
if ($layer->getValue() !== null && in_array($normalizedDefault, $combinedChoices, true) && !in_array($normalizedDefault, $groupDefaultValues, true)) {
153+
$groupDefaultValues[] = $normalizedDefault;
154+
}
155+
}
156+
157+
if (!$hasVisibleLayer || $combinedChoices === []) {
158+
return [$currentValue, $currentSourceScope, false, $visible && $hasVisibleLayer];
159+
}
160+
161+
$resolved->setAllowedValues($combinedChoices);
162+
163+
return [
164+
$this->pickValueChoiceDefault($definition, $currentValue, $combinedChoices, $groupDefaultValues, $context),
165+
'group',
166+
count($combinedChoices) > 1,
167+
true,
168+
];
169+
}
170+
102171
#[\Override]
103172
/** @param list<IPolicyDefinition> $definitions */
104173
public function resolveMany(array $definitions, PolicyContext $context): array {
@@ -126,13 +195,13 @@ private function applyLayer(
126195
$visible = $visible && $layer->isVisibleToChild();
127196
$resolved->setAllowedValues($this->mergeAllowedValues($resolved->getAllowedValues(), $layer->getAllowedValues()));
128197

129-
if ($layer->getValue() !== null && ($currentSourceScope === 'system' || $canOverrideBelow)) {
198+
if ($layer->getValue() !== null && $canOverrideBelow) {
130199
$currentValue = $definition->normalizeValue($layer->getValue());
131200
$definition->validateValue($currentValue, $context);
132201
$currentSourceScope = $layer->getScope();
133202
}
134203

135-
$canOverrideBelow = $layer->isAllowChildOverride();
204+
$canOverrideBelow = $canOverrideBelow && $layer->isAllowChildOverride();
136205

137206
return [$currentValue, $currentSourceScope, $canOverrideBelow, $visible];
138207
}
@@ -174,4 +243,90 @@ private function mergeAllowedValues(array $currentAllowedValues, array $layerAll
174243

175244
return array_values(array_intersect($currentAllowedValues, $layerAllowedValues));
176245
}
246+
247+
/**
248+
* @param list<mixed> $upstreamAllowedValues
249+
* @return list<mixed>
250+
*/
251+
private function resolveValueChoiceLayerChoices(
252+
IPolicyDefinition $definition,
253+
PolicyLayer $layer,
254+
array $upstreamAllowedValues,
255+
PolicyContext $context,
256+
): array {
257+
if ($layer->isAllowChildOverride()) {
258+
$choices = $layer->getAllowedValues() === []
259+
? $upstreamAllowedValues
260+
: array_values(array_intersect($upstreamAllowedValues, $layer->getAllowedValues()));
261+
262+
$defaultValue = $definition->normalizeValue($definition->defaultSystemValue());
263+
return array_values(array_filter(
264+
$choices,
265+
static fn (mixed $choice): bool => $choice !== $defaultValue,
266+
));
267+
}
268+
269+
if ($layer->getValue() === null) {
270+
return [];
271+
}
272+
273+
$value = $definition->normalizeValue($layer->getValue());
274+
if ($upstreamAllowedValues !== [] && !in_array($value, $upstreamAllowedValues, true)) {
275+
return [];
276+
}
277+
278+
$definition->validateValue($value, $context);
279+
return [$value];
280+
}
281+
282+
/**
283+
* @param list<mixed> $canonicalOrder
284+
* @param list<mixed> $currentValues
285+
* @param list<mixed> $newValues
286+
* @return list<mixed>
287+
*/
288+
private function mergeUnionAllowedValues(array $canonicalOrder, array $currentValues, array $newValues): array {
289+
$merged = [];
290+
foreach ($canonicalOrder as $candidate) {
291+
if ((in_array($candidate, $currentValues, true) || in_array($candidate, $newValues, true)) && !in_array($candidate, $merged, true)) {
292+
$merged[] = $candidate;
293+
}
294+
}
295+
296+
foreach ([$currentValues, $newValues] as $values) {
297+
foreach ($values as $candidate) {
298+
if (!in_array($candidate, $merged, true)) {
299+
$merged[] = $candidate;
300+
}
301+
}
302+
}
303+
304+
return $merged;
305+
}
306+
307+
/**
308+
* @param list<mixed> $allowedValues
309+
* @param list<mixed> $groupDefaultValues
310+
*/
311+
private function pickValueChoiceDefault(
312+
IPolicyDefinition $definition,
313+
mixed $currentValue,
314+
array $allowedValues,
315+
array $groupDefaultValues,
316+
PolicyContext $context,
317+
): mixed {
318+
$normalizedCurrentValue = $definition->normalizeValue($currentValue);
319+
$defaultValue = $definition->normalizeValue($definition->defaultSystemValue());
320+
321+
if (count($groupDefaultValues) === 1 && in_array($groupDefaultValues[0], $allowedValues, true)) {
322+
return $groupDefaultValues[0];
323+
}
324+
325+
if ($normalizedCurrentValue !== $defaultValue && in_array($normalizedCurrentValue, $allowedValues, true)) {
326+
return $normalizedCurrentValue;
327+
}
328+
329+
$orderedAllowedValues = $this->mergeUnionAllowedValues($definition->allowedValues($context), [], $allowedValues);
330+
return $orderedAllowedValues[0] ?? $normalizedCurrentValue;
331+
}
177332
}

0 commit comments

Comments
 (0)