From 9942f7ae817ee117b9fe7fba08d5b2d5f934148c Mon Sep 17 00:00:00 2001 From: Ahmed Darrazi Date: Tue, 30 Dec 2025 00:51:14 +0100 Subject: [PATCH] fix: readable normalized diff keys --- .../GroupPolicyConfigurationNormalizer.php | 25 +++- .../SettingsCatalogPolicyNormalizer.php | 117 +++++++++++++++++- ...pPolicyConfigurationNormalizedDiffTest.php | 29 +++++ ...ettingsCatalogPolicyNormalizedDiffTest.php | 55 ++++++++ 4 files changed, 222 insertions(+), 4 deletions(-) create mode 100644 tests/Feature/Filament/GroupPolicyConfigurationNormalizedDiffTest.php create mode 100644 tests/Feature/Filament/SettingsCatalogPolicyNormalizedDiffTest.php diff --git a/app/Services/Intune/GroupPolicyConfigurationNormalizer.php b/app/Services/Intune/GroupPolicyConfigurationNormalizer.php index c32892c..f54f058 100644 --- a/app/Services/Intune/GroupPolicyConfigurationNormalizer.php +++ b/app/Services/Intune/GroupPolicyConfigurationNormalizer.php @@ -3,7 +3,6 @@ namespace App\Services\Intune; use Illuminate\Support\Arr; -use Illuminate\Support\Str; class GroupPolicyConfigurationNormalizer implements PolicyTypeNormalizer { @@ -47,7 +46,12 @@ public function normalize(?array $snapshot, string $policyType, ?string $platfor $definitionId = $definitionValue['#Definition_Id'] ?? null; $category = $definitionValue['#Definition_categoryPath'] ?? '-'; $enabled = (bool) ($definitionValue['enabled'] ?? false); - $path = $definitionValue['definition@odata.bind'] ?? (is_string($definitionId) ? $definitionId : "definitionValues[{$index}]"); + $path = $this->buildDiffPath( + definition: $definition, + definitionId: $definitionId, + categoryPath: $category, + index: $index, + ); $value = $this->formatGroupPolicyValue($definitionValue, $enabled); $dataType = $this->inferGroupPolicyDataType($definitionValue); @@ -59,7 +63,7 @@ public function normalize(?array $snapshot, string $policyType, ?string $platfor 'data_type' => $dataType, 'value' => $value, 'description' => '-', - 'path' => is_string($path) ? Str::limit($path, 200) : "definitionValues[{$index}]", + 'path' => $path, 'raw' => $definitionValue, ]; } @@ -159,4 +163,19 @@ private function formatGroupPolicyValue(array $definitionValue, bool $enabled): return implode(' | ', array_values(array_filter($parts, static fn ($part) => $part !== ''))); } + + private function buildDiffPath(mixed $definition, mixed $definitionId, mixed $categoryPath, int $index): string + { + $label = is_string($definition) && $definition !== '' ? $definition : "definitionValues[{$index}]"; + + if (is_string($definitionId) && $definitionId !== '') { + $label .= " ({$definitionId})"; + } + + if (is_string($categoryPath) && $categoryPath !== '' && $categoryPath !== '-') { + return $categoryPath.' > '.$label; + } + + return $label; + } } diff --git a/app/Services/Intune/SettingsCatalogPolicyNormalizer.php b/app/Services/Intune/SettingsCatalogPolicyNormalizer.php index 6fc64c1..1b74907 100644 --- a/app/Services/Intune/SettingsCatalogPolicyNormalizer.php +++ b/app/Services/Intune/SettingsCatalogPolicyNormalizer.php @@ -26,6 +26,121 @@ public function normalize(?array $snapshot, string $policyType, ?string $platfor */ public function flattenForDiff(?array $snapshot, string $policyType, ?string $platform = null): array { - return $this->defaultNormalizer->flattenForDiff($snapshot, $policyType, $platform); + $normalized = $this->normalize($snapshot ?? [], $policyType, $platform); + + $map = []; + + if (isset($normalized['settings_table']['rows']) && is_array($normalized['settings_table']['rows'])) { + $title = $normalized['settings_table']['title'] ?? 'Settings'; + $prefix = is_string($title) && $title !== '' ? $title.' > ' : ''; + $rows = $normalized['settings_table']['rows']; + + $baseLabels = array_values(array_filter(array_map(function (mixed $row): ?string { + if (! is_array($row)) { + return null; + } + + return $this->buildSettingsCatalogDiffLabel($row, includePath: false); + }, $rows))); + + $labelCounts = array_count_values($baseLabels); + + foreach ($rows as $row) { + if (! is_array($row)) { + continue; + } + + $baseLabel = $this->buildSettingsCatalogDiffLabel($row, includePath: false); + $label = $baseLabel; + + if (($labelCounts[$baseLabel] ?? 0) > 1) { + $path = $row['path'] ?? null; + $pathLabel = is_string($path) && $path !== '' ? $path : null; + + $label = $this->buildSettingsCatalogDiffLabel($row, includePath: true); + + if ($pathLabel !== null) { + $label .= ' @ '.$pathLabel; + } + } + + $key = $prefix.$label; + $map[$key] = $row['value'] ?? null; + } + } + + foreach ($normalized['settings'] ?? [] as $block) { + if (! is_array($block)) { + continue; + } + + $title = $block['title'] ?? null; + $prefix = is_string($title) && $title !== '' ? $title.' > ' : ''; + + if (($block['type'] ?? null) === 'table') { + foreach ($block['rows'] ?? [] as $row) { + if (! is_array($row)) { + continue; + } + + $key = $prefix.($row['path'] ?? $row['label'] ?? 'entry'); + $map[$key] = $row['value'] ?? null; + } + + continue; + } + + foreach ($block['entries'] ?? [] as $entry) { + if (! is_array($entry)) { + continue; + } + + $key = $prefix.($entry['key'] ?? 'entry'); + $map[$key] = $entry['value'] ?? null; + } + } + + return $map; + } + + /** + * @param array $row + */ + private function buildSettingsCatalogDiffLabel(array $row, bool $includePath): string + { + $category = $row['category'] ?? null; + $definition = $row['definition'] ?? null; + $definitionId = $row['definition_id'] ?? null; + + $label = is_string($definition) && $definition !== '' ? $definition : 'Setting'; + + if ($includePath) { + $path = $row['path'] ?? null; + + if (is_string($path) && $path !== '') { + $label = $path; + } + + if ( + is_string($label) + && is_string($definitionId) + && $definitionId !== '' + && is_string($definition) + && $definition !== '' + ) { + $parts = explode(' > ', $label); + + if ($parts !== [] && end($parts) === $definitionId) { + $parts[count($parts) - 1] = $definition; + $label = implode(' > ', $parts); + } + } + } + + if (is_string($category) && $category !== '' && $category !== '-') { + $label = $category.' > '.$label; + } + + return $label; } } diff --git a/tests/Feature/Filament/GroupPolicyConfigurationNormalizedDiffTest.php b/tests/Feature/Filament/GroupPolicyConfigurationNormalizedDiffTest.php new file mode 100644 index 0000000..24e1500 --- /dev/null +++ b/tests/Feature/Filament/GroupPolicyConfigurationNormalizedDiffTest.php @@ -0,0 +1,29 @@ +flattenForDiff( + snapshot: [ + 'id' => 'gpo-1', + 'displayName' => 'Admin Templates Alpha', + '@odata.type' => '#microsoft.graph.groupPolicyConfiguration', + 'definitionValues' => [ + [ + 'enabled' => true, + 'definition@odata.bind' => 'https://graph.microsoft.com/beta/deviceManagement/groupPolicyDefinitions(\'def-1\')', + '#Definition_Id' => 'def-1', + '#Definition_displayName' => 'Block legacy auth', + '#Definition_categoryPath' => 'Windows Components\\Security Options', + ], + ], + ], + policyType: 'groupPolicyConfiguration', + platform: 'windows', + ); + + $keys = array_keys($flat); + + expect($keys)->toContain('Administrative Template settings > Windows Components\\Security Options > Block legacy auth (def-1)'); + expect(implode("\n", $keys))->not->toContain('graph.microsoft.com'); +}); diff --git a/tests/Feature/Filament/SettingsCatalogPolicyNormalizedDiffTest.php b/tests/Feature/Filament/SettingsCatalogPolicyNormalizedDiffTest.php new file mode 100644 index 0000000..3c34b38 --- /dev/null +++ b/tests/Feature/Filament/SettingsCatalogPolicyNormalizedDiffTest.php @@ -0,0 +1,55 @@ + 'cat-1', + 'display_name' => 'Account Management', + 'description' => null, + ]); + + SettingsCatalogDefinition::create([ + 'definition_id' => 'device_vendor_msft_accountmanagement_userprofilemanagement_deletionpolicy', + 'display_name' => 'Deletion Policy', + 'description' => null, + 'help_text' => null, + 'category_id' => 'cat-1', + 'ux_behavior' => null, + 'raw' => [], + ]); + + $flat = app(PolicyNormalizer::class)->flattenForDiff( + snapshot: [ + '@odata.type' => '#microsoft.graph.deviceManagementConfigurationPolicy', + 'id' => 'scp-policy-1', + 'name' => 'Settings Catalog Policy', + 'platforms' => 'windows10', + 'technologies' => 'mdm', + 'settings' => [ + [ + 'id' => 's1', + 'settingInstance' => [ + '@odata.type' => '#microsoft.graph.deviceManagementConfigurationChoiceSettingInstance', + 'settingDefinitionId' => 'device_vendor_msft_accountmanagement_userprofilemanagement_deletionpolicy', + 'choiceSettingValue' => [ + 'value' => 'enabled', + ], + ], + ], + ], + ], + policyType: 'settingsCatalogPolicy', + platform: 'windows', + ); + + $keys = array_keys($flat); + + expect($keys)->toContain('Settings > Account Management > Deletion Policy'); + expect(implode("\n", $keys))->not->toContain('device_vendor_msft_accountmanagement_userprofilemanagement_deletionpolicy'); +});