From 59c74246d6e4111b89a7195ae58465ad0e3413c9 Mon Sep 17 00:00:00 2001 From: Ahmed Darrazi Date: Sun, 28 Dec 2025 23:08:09 +0100 Subject: [PATCH] fix: allow nested compliance action expand --- app/Services/Graph/GraphContractRegistry.php | 31 +++++++++++++++---- app/Services/Intune/PolicySnapshotService.php | 2 +- config/graph_contracts.php | 5 ++- tests/Unit/PolicySnapshotServiceTest.php | 3 +- 4 files changed, 32 insertions(+), 9 deletions(-) diff --git a/app/Services/Graph/GraphContractRegistry.php b/app/Services/Graph/GraphContractRegistry.php index 37afda0..abf23f0 100644 --- a/app/Services/Graph/GraphContractRegistry.php +++ b/app/Services/Graph/GraphContractRegistry.php @@ -25,22 +25,41 @@ public function sanitizeQuery(string $policyType, array $query): array $allowedExpand = $contract['allowed_expand'] ?? []; $warnings = []; - if (! empty($query['$select']) && is_array($query['$select'])) { + if (! empty($query['$select'])) { $original = $query['$select']; - $query['$select'] = array_values(array_intersect($original, $allowedSelect)); + $select = is_array($original) + ? $original + : array_map('trim', explode(',', (string) $original)); + $filtered = array_values(array_intersect($select, $allowedSelect)); - if (count($query['$select']) !== count($original)) { + if (count($filtered) !== count($select)) { $warnings[] = 'Trimmed unsupported $select fields for capability safety.'; } + + if ($filtered === []) { + unset($query['$select']); + } else { + $query['$select'] = implode(',', $filtered); + } } - if (! empty($query['$expand']) && is_array($query['$expand'])) { + if (! empty($query['$expand'])) { $original = $query['$expand']; - $query['$expand'] = array_values(array_intersect($original, $allowedExpand)); + $expand = is_array($original) + ? $original + : [trim((string) $original)]; + $expand = array_values(array_filter($expand, static fn ($value) => $value !== '')); + $filtered = array_values(array_intersect($expand, $allowedExpand)); - if (count($query['$expand']) !== count($original)) { + if (count($filtered) !== count($expand)) { $warnings[] = 'Trimmed unsupported $expand fields for capability safety.'; } + + if ($filtered === []) { + unset($query['$expand']); + } else { + $query['$expand'] = implode(',', $filtered); + } } return [ diff --git a/app/Services/Intune/PolicySnapshotService.php b/app/Services/Intune/PolicySnapshotService.php index 5caa176..031c561 100644 --- a/app/Services/Intune/PolicySnapshotService.php +++ b/app/Services/Intune/PolicySnapshotService.php @@ -47,7 +47,7 @@ public function fetch(Tenant $tenant, Policy $policy, ?string $actorEmail = null ]; if ($policy->policy_type === 'deviceCompliancePolicy') { - $options['expand'] = ['scheduledActionsForRule']; + $options['expand'] = 'scheduledActionsForRule($expand=scheduledActionConfigurations)'; } $response = $this->graphClient->getPolicy($policy->policy_type, $policy->external_id, $options); diff --git a/config/graph_contracts.php b/config/graph_contracts.php index 3e86656..67d46eb 100644 --- a/config/graph_contracts.php +++ b/config/graph_contracts.php @@ -15,7 +15,10 @@ 'deviceConfiguration' => [ 'resource' => 'deviceManagement/deviceConfigurations', 'allowed_select' => ['id', 'displayName', 'description', '@odata.type', 'version', 'lastModifiedDateTime'], - 'allowed_expand' => ['scheduledActionsForRule'], + 'allowed_expand' => [ + 'scheduledActionsForRule', + 'scheduledActionsForRule($expand=scheduledActionConfigurations)', + ], 'type_family' => [ '#microsoft.graph.deviceConfiguration', '#microsoft.graph.windows10CustomConfiguration', diff --git a/tests/Unit/PolicySnapshotServiceTest.php b/tests/Unit/PolicySnapshotServiceTest.php index aaee4b0..93cbed1 100644 --- a/tests/Unit/PolicySnapshotServiceTest.php +++ b/tests/Unit/PolicySnapshotServiceTest.php @@ -104,5 +104,6 @@ public function request(string $method, string $path, array $options = []): Grap expect($client->requests[0][0])->toBe('getPolicy'); expect($client->requests[0][1])->toBe('deviceCompliancePolicy'); expect($client->requests[0][2])->toBe('compliance-123'); - expect($client->requests[0][3]['expand'] ?? [])->toBe(['scheduledActionsForRule']); + expect($client->requests[0][3]['expand'] ?? null) + ->toBe('scheduledActionsForRule($expand=scheduledActionConfigurations)'); });