TenantAtlas/app/Services/Graph/AssignmentFetcher.php
2025-12-27 22:32:51 +01:00

148 lines
4.5 KiB
PHP

<?php
namespace App\Services\Graph;
use Illuminate\Support\Facades\Log;
class AssignmentFetcher
{
public function __construct(
private readonly MicrosoftGraphClient $graphClient,
private readonly GraphContractRegistry $contracts,
) {}
/**
* Fetch policy assignments with fallback strategy.
*
* Primary: GET {assignments_list_path}
* Fallback: GET {resource}?$expand=assignments&$filter=id eq '{id}'
*
* @return array Returns assignment array or empty array on failure
*/
public function fetch(string $policyType, string $tenantId, string $policyId, array $options = []): array
{
try {
$contract = $this->contracts->get($policyType);
$listPathTemplate = $contract['assignments_list_path'] ?? null;
$resource = $contract['resource'] ?? null;
$requestOptions = array_merge($options, ['tenant' => $tenantId]);
// Try primary endpoint
$assignments = $this->fetchPrimary($listPathTemplate, $policyId, $requestOptions);
if (! empty($assignments)) {
Log::debug('Fetched assignments via primary endpoint', [
'tenant_id' => $tenantId,
'policy_type' => $policyType,
'policy_id' => $policyId,
'count' => count($assignments),
]);
return $assignments;
}
// Try fallback with $expand
Log::debug('Primary endpoint returned empty, trying fallback', [
'tenant_id' => $tenantId,
'policy_type' => $policyType,
'policy_id' => $policyId,
]);
if (! is_string($resource) || $resource === '') {
Log::debug('Assignments resource not configured for policy type', [
'tenant_id' => $tenantId,
'policy_type' => $policyType,
'policy_id' => $policyId,
]);
return [];
}
$assignments = $this->fetchWithExpand($resource, $policyId, $requestOptions);
if (! empty($assignments)) {
Log::debug('Fetched assignments via fallback endpoint', [
'tenant_id' => $tenantId,
'policy_type' => $policyType,
'policy_id' => $policyId,
'count' => count($assignments),
]);
return $assignments;
}
// Both methods returned empty
Log::debug('No assignments found for policy', [
'tenant_id' => $tenantId,
'policy_type' => $policyType,
'policy_id' => $policyId,
]);
return [];
} catch (GraphException $e) {
Log::warning('Failed to fetch assignments', [
'tenant_id' => $tenantId,
'policy_type' => $policyType,
'policy_id' => $policyId,
'error' => $e->getMessage(),
'context' => $e->context,
]);
return [];
}
}
/**
* Fetch assignments using primary endpoint.
*/
private function fetchPrimary(?string $listPathTemplate, string $policyId, array $options): array
{
if (! is_string($listPathTemplate) || $listPathTemplate === '') {
return [];
}
$path = $this->resolvePath($listPathTemplate, $policyId);
if ($path === null) {
return [];
}
$response = $this->graphClient->request('GET', $path, $options);
return $response->data['value'] ?? [];
}
/**
* Fetch assignments using $expand fallback.
*/
private function fetchWithExpand(string $resource, string $policyId, array $options): array
{
$path = $resource;
$params = [
'$expand' => 'assignments',
'$filter' => "id eq '{$policyId}'",
];
$response = $this->graphClient->request('GET', $path, array_merge($options, [
'query' => $params,
]));
$policies = $response->data['value'] ?? [];
if (empty($policies)) {
return [];
}
return $policies[0]['assignments'] ?? [];
}
private function resolvePath(string $template, string $policyId): ?string
{
if ($template === '') {
return null;
}
return str_replace('{id}', urlencode($policyId), $template);
}
}