fix: avoid assignment expand fallback

This commit is contained in:
Ahmed Darrazi 2026-01-01 13:07:06 +01:00
parent c749e22bee
commit 058724c359
2 changed files with 49 additions and 29 deletions

View File

@ -39,7 +39,7 @@ public function fetch(
$primaryException = null; $primaryException = null;
$assignments = []; $assignments = [];
$primarySucceeded = false; $lastSuccessfulAssignments = null;
// Try primary endpoint(s) // Try primary endpoint(s)
$listPathTemplates = []; $listPathTemplates = [];
@ -65,7 +65,12 @@ public function fetch(
$context, $context,
$throwOnFailure $throwOnFailure
); );
$primarySucceeded = true;
if ($assignments === null) {
continue;
}
$lastSuccessfulAssignments = $assignments;
if (! empty($assignments)) { if (! empty($assignments)) {
Log::debug('Fetched assignments via primary endpoint', [ Log::debug('Fetched assignments via primary endpoint', [
@ -77,20 +82,25 @@ public function fetch(
return $assignments; return $assignments;
} }
if ($policyType !== 'appProtectionPolicy') {
// Empty is a valid outcome (policy not assigned). Do not attempt fallback.
return [];
}
} catch (GraphException $e) { } catch (GraphException $e) {
$primaryException = $primaryException ?? $e; $primaryException = $primaryException ?? $e;
} }
} }
if ($primarySucceeded && $policyType === 'appProtectionPolicy') { if ($lastSuccessfulAssignments !== null && $policyType === 'appProtectionPolicy') {
Log::debug('Assignments fetched via primary endpoint(s)', [ Log::debug('Assignments fetched via primary endpoint(s)', [
'tenant_id' => $tenantId, 'tenant_id' => $tenantId,
'policy_type' => $policyType, 'policy_type' => $policyType,
'policy_id' => $policyId, 'policy_id' => $policyId,
'count' => count($assignments), 'count' => count($lastSuccessfulAssignments),
]); ]);
return $assignments; return $lastSuccessfulAssignments;
} }
// Try fallback with $expand // Try fallback with $expand
@ -215,15 +225,15 @@ private function fetchPrimary(
array $options, array $options,
array $context, array $context,
bool $throwOnFailure bool $throwOnFailure
): array { ): ?array {
if (! is_string($listPathTemplate) || $listPathTemplate === '') { if (! is_string($listPathTemplate) || $listPathTemplate === '') {
return []; return null;
} }
$path = $this->resolvePath($listPathTemplate, $policyId); $path = $this->resolvePath($listPathTemplate, $policyId);
if ($path === null) { if ($path === null) {
return []; return null;
} }
$response = $this->graphClient->request('GET', $path, $options); $response = $this->graphClient->request('GET', $path, $options);
@ -239,7 +249,7 @@ private function fetchPrimary(
); );
} }
return []; return null;
} }
return $response->data['value'] ?? []; return $response->data['value'] ?? [];

View File

@ -75,15 +75,11 @@
expect($result)->toBe($assignments); expect($result)->toBe($assignments);
}); });
test('fallback on empty response', function () { test('does not use fallback when primary succeeds with empty assignments', function () {
$tenantId = 'tenant-123'; $tenantId = 'tenant-123';
$policyId = 'policy-456'; $policyId = 'policy-456';
$policyType = 'settingsCatalogPolicy'; $policyType = 'settingsCatalogPolicy';
$assignments = [
['id' => 'assign-1', 'target' => ['@odata.type' => '#microsoft.graph.groupAssignmentTarget', 'groupId' => 'group-1']],
];
// Primary returns empty
$primaryResponse = new GraphResponse( $primaryResponse = new GraphResponse(
success: true, success: true,
data: ['value' => []] data: ['value' => []]
@ -97,7 +93,34 @@
]) ])
->andReturn($primaryResponse); ->andReturn($primaryResponse);
// Fallback returns assignments $result = $this->fetcher->fetch($policyType, $tenantId, $policyId);
expect($result)->toBe([]);
});
test('uses fallback when primary endpoint fails', function () {
$tenantId = 'tenant-123';
$policyId = 'policy-456';
$policyType = 'settingsCatalogPolicy';
$assignments = [
['id' => 'assign-1', 'target' => ['@odata.type' => '#microsoft.graph.groupAssignmentTarget', 'groupId' => 'group-1']],
];
$primaryFailure = new GraphResponse(
success: false,
data: [],
status: 400,
errors: [['message' => 'Bad Request']]
);
$this->graphClient
->shouldReceive('request')
->once()
->with('GET', "/deviceManagement/configurationPolicies/{$policyId}/assignments", [
'tenant' => $tenantId,
])
->andReturn($primaryFailure);
$fallbackResponse = new GraphResponse( $fallbackResponse = new GraphResponse(
success: true, success: true,
data: ['value' => [['id' => $policyId, 'assignments' => $assignments]]] data: ['value' => [['id' => $policyId, 'assignments' => $assignments]]]
@ -152,18 +175,6 @@
->with('GET', "/deviceManagement/configurationPolicies/{$policyId}/assignments", Mockery::any()) ->with('GET', "/deviceManagement/configurationPolicies/{$policyId}/assignments", Mockery::any())
->andReturn($primaryResponse); ->andReturn($primaryResponse);
// Fallback returns empty
$fallbackResponse = new GraphResponse(
success: true,
data: ['value' => []]
);
$this->graphClient
->shouldReceive('request')
->once()
->with('GET', 'deviceManagement/configurationPolicies', Mockery::any())
->andReturn($fallbackResponse);
$result = $this->fetcher->fetch($policyType, $tenantId, $policyId); $result = $this->fetcher->fetch($policyType, $tenantId, $policyId);
expect($result)->toBe([]); expect($result)->toBe([]);
@ -174,9 +185,8 @@
$policyId = 'policy-456'; $policyId = 'policy-456';
$policyType = 'settingsCatalogPolicy'; $policyType = 'settingsCatalogPolicy';
// Primary returns empty
$primaryResponse = new GraphResponse( $primaryResponse = new GraphResponse(
success: true, success: false,
data: ['value' => []] data: ['value' => []]
); );