['id' => ..., 'displayName' => ..., 'orphaned' => bool]] */ public function resolveGroupIds(array $groupIds, string $tenantId): array { if (empty($groupIds)) { return []; } // Create cache key $cacheKey = $this->getCacheKey($groupIds, $tenantId); return Cache::remember($cacheKey, 300, function () use ($groupIds, $tenantId) { return $this->fetchAndResolveGroups($groupIds, $tenantId); }); } /** * Fetch groups from Graph API and resolve orphaned IDs. */ private function fetchAndResolveGroups(array $groupIds, string $tenantId): array { try { $response = $this->graphClient->post( '/directoryObjects/getByIds', [ 'ids' => array_values($groupIds), 'types' => ['group'], ], $tenantId ); $resolvedGroups = $response['value'] ?? []; // Create result map $result = []; $resolvedIds = []; // Add resolved groups foreach ($resolvedGroups as $group) { $groupId = $group['id']; $resolvedIds[] = $groupId; $result[$groupId] = [ 'id' => $groupId, 'displayName' => $group['displayName'] ?? null, 'orphaned' => false, ]; } // Add orphaned groups (not in response) foreach ($groupIds as $groupId) { if (! in_array($groupId, $resolvedIds)) { $result[$groupId] = [ 'id' => $groupId, 'displayName' => null, 'orphaned' => true, ]; } } $this->logger->logDebug('Resolved group IDs', [ 'tenant_id' => $tenantId, 'requested' => count($groupIds), 'resolved' => count($resolvedIds), 'orphaned' => count($groupIds) - count($resolvedIds), ]); return $result; } catch (GraphException $e) { $this->logger->logWarning('Failed to resolve group IDs', [ 'tenant_id' => $tenantId, 'group_ids' => $groupIds, 'error' => $e->getMessage(), 'context' => $e->context, ]); // Return all as orphaned on failure $result = []; foreach ($groupIds as $groupId) { $result[$groupId] = [ 'id' => $groupId, 'displayName' => null, 'orphaned' => true, ]; } return $result; } } /** * Generate cache key for group resolution. */ private function getCacheKey(array $groupIds, string $tenantId): string { sort($groupIds); return "groups:{$tenantId}:".md5(implode(',', $groupIds)); } }