TenantAtlas/apps/platform/app/Services/Graph/GroupResolver.php
ahmido ce0615a9c1 Spec 182: relocate Laravel platform to apps/platform (#213)
## Summary
- move the Laravel application into `apps/platform` and keep the repository root for orchestration, docs, and tooling
- update the local command model, Sail/Docker wiring, runtime paths, and ignore rules around the new platform location
- add relocation quickstart/contracts plus focused smoke coverage for bootstrap, command model, routes, and runtime behavior

## Validation
- `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/PlatformRelocation`
- integrated browser smoke validated `/up`, `/`, `/admin`, `/admin/choose-workspace`, and tenant route semantics for `200`, `403`, and `404`

## Remaining Rollout Checks
- validate Dokploy build context and working-directory assumptions against the new `apps/platform` layout
- confirm web, queue, and scheduler processes all start from the expected working directory in staging/production
- verify no legacy volume mounts or asset-publish paths still point at the old root-level `public/` or `storage/` locations

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #213
2026-04-08 08:40:47 +00:00

124 lines
3.7 KiB
PHP

<?php
namespace App\Services\Graph;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log;
class GroupResolver
{
public function __construct(
private readonly GraphClientInterface $graphClient,
) {}
/**
* Resolve group IDs to group objects with display names.
*
* Uses POST /directoryObjects/getByIds endpoint.
* Missing IDs are marked as orphaned.
*
* @param array $groupIds Array of group IDs to resolve
* @param string $tenantId Target tenant ID
* @return array Keyed array: ['group-id' => ['id' => ..., 'displayName' => ..., 'orphaned' => bool]]
*/
public function resolveGroupIds(array $groupIds, string $tenantId, array $options = []): array
{
if (empty($groupIds)) {
return [];
}
// Create cache key
$cacheKey = $this->getCacheKey($groupIds, $tenantId);
return Cache::remember($cacheKey, 300, function () use ($groupIds, $tenantId, $options) {
return $this->fetchAndResolveGroups($groupIds, $tenantId, $options);
});
}
/**
* Fetch groups from Graph API and resolve orphaned IDs.
*/
private function fetchAndResolveGroups(array $groupIds, string $tenantId, array $options = []): array
{
try {
$response = $this->graphClient->request(
'POST',
'/directoryObjects/getByIds',
array_merge($options, [
'tenant' => $tenantId,
'json' => [
'ids' => array_values($groupIds),
'types' => ['group'],
],
])
);
$resolvedGroups = $response->data['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,
];
}
}
Log::debug('Resolved group IDs', [
'tenant_id' => $tenantId,
'requested' => count($groupIds),
'resolved' => count($resolvedIds),
'orphaned' => count($groupIds) - count($resolvedIds),
]);
return $result;
} catch (GraphException $e) {
Log::warning('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));
}
}