merge: agent session work
This commit is contained in:
commit
0f8ecfe470
@ -5,6 +5,7 @@
|
|||||||
use App\Models\BackupItem;
|
use App\Models\BackupItem;
|
||||||
use App\Models\Tenant;
|
use App\Models\Tenant;
|
||||||
use App\Services\Graph\AssignmentFetcher;
|
use App\Services\Graph\AssignmentFetcher;
|
||||||
|
use App\Services\Graph\AssignmentFilterResolver;
|
||||||
use App\Services\Graph\GroupResolver;
|
use App\Services\Graph\GroupResolver;
|
||||||
use App\Services\Graph\ScopeTagResolver;
|
use App\Services\Graph\ScopeTagResolver;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
@ -14,6 +15,7 @@ class AssignmentBackupService
|
|||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly AssignmentFetcher $assignmentFetcher,
|
private readonly AssignmentFetcher $assignmentFetcher,
|
||||||
private readonly GroupResolver $groupResolver,
|
private readonly GroupResolver $groupResolver,
|
||||||
|
private readonly AssignmentFilterResolver $assignmentFilterResolver,
|
||||||
private readonly ScopeTagResolver $scopeTagResolver,
|
private readonly ScopeTagResolver $scopeTagResolver,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@ -80,13 +82,29 @@ public function enrichWithAssignments(
|
|||||||
|
|
||||||
// Extract group IDs and resolve for orphan detection
|
// Extract group IDs and resolve for orphan detection
|
||||||
$groupIds = $this->extractGroupIds($assignments);
|
$groupIds = $this->extractGroupIds($assignments);
|
||||||
|
$resolvedGroups = [];
|
||||||
$hasOrphanedGroups = false;
|
$hasOrphanedGroups = false;
|
||||||
|
|
||||||
if (! empty($groupIds)) {
|
if (! empty($groupIds)) {
|
||||||
$resolvedGroups = $this->groupResolver->resolveGroupIds($groupIds, $tenantId, $graphOptions);
|
$resolvedGroups = $this->groupResolver->resolveGroupIds($groupIds, $tenantId, $graphOptions);
|
||||||
$hasOrphanedGroups = collect($resolvedGroups)->contains('orphaned', true);
|
$hasOrphanedGroups = collect($resolvedGroups)
|
||||||
|
->contains(fn (array $group) => $group['orphaned'] ?? false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$filterIds = collect($assignments)
|
||||||
|
->pluck('target.deviceAndAppManagementAssignmentFilterId')
|
||||||
|
->filter()
|
||||||
|
->unique()
|
||||||
|
->values()
|
||||||
|
->all();
|
||||||
|
|
||||||
|
$filters = $this->assignmentFilterResolver->resolve($filterIds, $tenant);
|
||||||
|
$filterNames = collect($filters)
|
||||||
|
->pluck('displayName', 'id')
|
||||||
|
->all();
|
||||||
|
|
||||||
|
$assignments = $this->enrichAssignments($assignments, $resolvedGroups, $filterNames);
|
||||||
|
|
||||||
// Update backup item with assignments and metadata
|
// Update backup item with assignments and metadata
|
||||||
$metadata['assignment_count'] = count($assignments);
|
$metadata['assignment_count'] = count($assignments);
|
||||||
$metadata['assignments_fetch_failed'] = false;
|
$metadata['assignments_fetch_failed'] = false;
|
||||||
@ -135,11 +153,42 @@ private function extractGroupIds(array $assignments): array
|
|||||||
$target = $assignment['target'] ?? [];
|
$target = $assignment['target'] ?? [];
|
||||||
$odataType = $target['@odata.type'] ?? '';
|
$odataType = $target['@odata.type'] ?? '';
|
||||||
|
|
||||||
if ($odataType === '#microsoft.graph.groupAssignmentTarget' && isset($target['groupId'])) {
|
if (in_array($odataType, [
|
||||||
|
'#microsoft.graph.groupAssignmentTarget',
|
||||||
|
'#microsoft.graph.exclusionGroupAssignmentTarget',
|
||||||
|
], true) && isset($target['groupId'])) {
|
||||||
$groupIds[] = $target['groupId'];
|
$groupIds[] = $target['groupId'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return array_unique($groupIds);
|
return array_unique($groupIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<int, array<string, mixed>> $assignments
|
||||||
|
* @param array<string, array{id:string,displayName:?string,orphaned:bool}> $groups
|
||||||
|
* @param array<string, string> $filterNames
|
||||||
|
* @return array<int, array<string, mixed>>
|
||||||
|
*/
|
||||||
|
private function enrichAssignments(array $assignments, array $groups, array $filterNames): array
|
||||||
|
{
|
||||||
|
return array_map(function (array $assignment) use ($groups, $filterNames): array {
|
||||||
|
$target = $assignment['target'] ?? [];
|
||||||
|
$groupId = $target['groupId'] ?? null;
|
||||||
|
|
||||||
|
if ($groupId && isset($groups[$groupId])) {
|
||||||
|
$target['group_display_name'] = $groups[$groupId]['displayName'] ?? null;
|
||||||
|
$target['group_orphaned'] = $groups[$groupId]['orphaned'] ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$filterId = $target['deviceAndAppManagementAssignmentFilterId'] ?? null;
|
||||||
|
if ($filterId && isset($filterNames[$filterId])) {
|
||||||
|
$target['assignment_filter_name'] = $filterNames[$filterId];
|
||||||
|
}
|
||||||
|
|
||||||
|
$assignment['target'] = $target;
|
||||||
|
|
||||||
|
return $assignment;
|
||||||
|
}, $assignments);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
54
app/Services/Graph/AssignmentFilterResolver.php
Normal file
54
app/Services/Graph/AssignmentFilterResolver.php
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Services\Graph;
|
||||||
|
|
||||||
|
use App\Models\Tenant;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
|
||||||
|
class AssignmentFilterResolver
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private readonly MicrosoftGraphClient $graphClient,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<int, string> $filterIds
|
||||||
|
* @return array<int, array{id:string,displayName:?string}>
|
||||||
|
*/
|
||||||
|
public function resolve(array $filterIds, ?Tenant $tenant = null): array
|
||||||
|
{
|
||||||
|
if (empty($filterIds)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$allFilters = $this->fetchAllFilters($tenant);
|
||||||
|
|
||||||
|
return array_values(array_filter($allFilters, function (array $filter) use ($filterIds): bool {
|
||||||
|
return in_array($filter['id'] ?? null, $filterIds, true);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<int, array{id:string,displayName:?string}>
|
||||||
|
*/
|
||||||
|
private function fetchAllFilters(?Tenant $tenant = null): array
|
||||||
|
{
|
||||||
|
$cacheKey = $tenant ? "assignment_filters:tenant:{$tenant->id}" : 'assignment_filters:all';
|
||||||
|
|
||||||
|
return Cache::remember($cacheKey, 3600, function () use ($tenant): array {
|
||||||
|
$options = ['query' => ['$select' => 'id,displayName']];
|
||||||
|
|
||||||
|
if ($tenant) {
|
||||||
|
$options = array_merge($options, $tenant->graphOptions());
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = $this->graphClient->request(
|
||||||
|
'GET',
|
||||||
|
'/deviceManagement/assignmentFilters',
|
||||||
|
$options
|
||||||
|
);
|
||||||
|
|
||||||
|
return $response->data['value'] ?? [];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -6,6 +6,7 @@
|
|||||||
use App\Models\PolicyVersion;
|
use App\Models\PolicyVersion;
|
||||||
use App\Models\Tenant;
|
use App\Models\Tenant;
|
||||||
use App\Services\Graph\AssignmentFetcher;
|
use App\Services\Graph\AssignmentFetcher;
|
||||||
|
use App\Services\Graph\AssignmentFilterResolver;
|
||||||
use App\Services\Graph\GroupResolver;
|
use App\Services\Graph\GroupResolver;
|
||||||
use App\Services\Graph\ScopeTagResolver;
|
use App\Services\Graph\ScopeTagResolver;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
@ -22,6 +23,7 @@ public function __construct(
|
|||||||
private readonly PolicySnapshotService $snapshotService,
|
private readonly PolicySnapshotService $snapshotService,
|
||||||
private readonly AssignmentFetcher $assignmentFetcher,
|
private readonly AssignmentFetcher $assignmentFetcher,
|
||||||
private readonly GroupResolver $groupResolver,
|
private readonly GroupResolver $groupResolver,
|
||||||
|
private readonly AssignmentFilterResolver $assignmentFilterResolver,
|
||||||
private readonly ScopeTagResolver $scopeTagResolver,
|
private readonly ScopeTagResolver $scopeTagResolver,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@ -59,7 +61,7 @@ public function capture(
|
|||||||
$rawAssignments = $this->assignmentFetcher->fetch($tenantIdentifier, $policy->external_id, $graphOptions);
|
$rawAssignments = $this->assignmentFetcher->fetch($tenantIdentifier, $policy->external_id, $graphOptions);
|
||||||
|
|
||||||
if (! empty($rawAssignments)) {
|
if (! empty($rawAssignments)) {
|
||||||
$assignments = $rawAssignments;
|
$resolvedGroups = [];
|
||||||
|
|
||||||
// Resolve groups for orphaned detection
|
// Resolve groups for orphaned detection
|
||||||
$groupIds = collect($rawAssignments)
|
$groupIds = collect($rawAssignments)
|
||||||
@ -75,6 +77,19 @@ public function capture(
|
|||||||
->contains(fn (array $group) => $group['orphaned'] ?? false);
|
->contains(fn (array $group) => $group['orphaned'] ?? false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$filterIds = collect($rawAssignments)
|
||||||
|
->pluck('target.deviceAndAppManagementAssignmentFilterId')
|
||||||
|
->filter()
|
||||||
|
->unique()
|
||||||
|
->values()
|
||||||
|
->all();
|
||||||
|
|
||||||
|
$filters = $this->assignmentFilterResolver->resolve($filterIds, $tenant);
|
||||||
|
$filterNames = collect($filters)
|
||||||
|
->pluck('displayName', 'id')
|
||||||
|
->all();
|
||||||
|
|
||||||
|
$assignments = $this->enrichAssignments($rawAssignments, $resolvedGroups, $filterNames);
|
||||||
$captureMetadata['assignments_count'] = count($rawAssignments);
|
$captureMetadata['assignments_count'] = count($rawAssignments);
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
@ -230,7 +245,7 @@ public function ensureVersionHasAssignments(
|
|||||||
$rawAssignments = $this->assignmentFetcher->fetch($tenantIdentifier, $policy->external_id, $graphOptions);
|
$rawAssignments = $this->assignmentFetcher->fetch($tenantIdentifier, $policy->external_id, $graphOptions);
|
||||||
|
|
||||||
if (! empty($rawAssignments)) {
|
if (! empty($rawAssignments)) {
|
||||||
$assignments = $rawAssignments;
|
$resolvedGroups = [];
|
||||||
|
|
||||||
// Resolve groups
|
// Resolve groups
|
||||||
$groupIds = collect($rawAssignments)
|
$groupIds = collect($rawAssignments)
|
||||||
@ -246,6 +261,19 @@ public function ensureVersionHasAssignments(
|
|||||||
->contains(fn (array $group) => $group['orphaned'] ?? false);
|
->contains(fn (array $group) => $group['orphaned'] ?? false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$filterIds = collect($rawAssignments)
|
||||||
|
->pluck('target.deviceAndAppManagementAssignmentFilterId')
|
||||||
|
->filter()
|
||||||
|
->unique()
|
||||||
|
->values()
|
||||||
|
->all();
|
||||||
|
|
||||||
|
$filters = $this->assignmentFilterResolver->resolve($filterIds, $tenant);
|
||||||
|
$filterNames = collect($filters)
|
||||||
|
->pluck('displayName', 'id')
|
||||||
|
->all();
|
||||||
|
|
||||||
|
$assignments = $this->enrichAssignments($rawAssignments, $resolvedGroups, $filterNames);
|
||||||
$metadata['assignments_count'] = count($rawAssignments);
|
$metadata['assignments_count'] = count($rawAssignments);
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
@ -291,6 +319,34 @@ public function ensureVersionHasAssignments(
|
|||||||
return $version->refresh();
|
return $version->refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<int, array<string, mixed>> $assignments
|
||||||
|
* @param array<string, array{id:string,displayName:?string,orphaned:bool}> $groups
|
||||||
|
* @param array<string, string> $filterNames
|
||||||
|
* @return array<int, array<string, mixed>>
|
||||||
|
*/
|
||||||
|
private function enrichAssignments(array $assignments, array $groups, array $filterNames): array
|
||||||
|
{
|
||||||
|
return array_map(function (array $assignment) use ($groups, $filterNames): array {
|
||||||
|
$target = $assignment['target'] ?? [];
|
||||||
|
$groupId = $target['groupId'] ?? null;
|
||||||
|
|
||||||
|
if ($groupId && isset($groups[$groupId])) {
|
||||||
|
$target['group_display_name'] = $groups[$groupId]['displayName'] ?? null;
|
||||||
|
$target['group_orphaned'] = $groups[$groupId]['orphaned'] ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$filterId = $target['deviceAndAppManagementAssignmentFilterId'] ?? null;
|
||||||
|
if ($filterId && isset($filterNames[$filterId])) {
|
||||||
|
$target['assignment_filter_name'] = $filterNames[$filterId];
|
||||||
|
}
|
||||||
|
|
||||||
|
$assignment['target'] = $target;
|
||||||
|
|
||||||
|
return $assignment;
|
||||||
|
}, $assignments);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array<int, string> $scopeTagIds
|
* @param array<int, string> $scopeTagIds
|
||||||
* @return array{ids:array<int, string>,names:array<int, string>}
|
* @return array{ids:array<int, string>,names:array<int, string>}
|
||||||
|
|||||||
@ -6,6 +6,7 @@
|
|||||||
use App\Models\PolicyVersion;
|
use App\Models\PolicyVersion;
|
||||||
use App\Models\Tenant;
|
use App\Models\Tenant;
|
||||||
use App\Services\Graph\AssignmentFetcher;
|
use App\Services\Graph\AssignmentFetcher;
|
||||||
|
use App\Services\Graph\AssignmentFilterResolver;
|
||||||
use App\Services\Graph\GroupResolver;
|
use App\Services\Graph\GroupResolver;
|
||||||
use App\Services\Graph\ScopeTagResolver;
|
use App\Services\Graph\ScopeTagResolver;
|
||||||
use Carbon\CarbonImmutable;
|
use Carbon\CarbonImmutable;
|
||||||
@ -17,6 +18,7 @@ public function __construct(
|
|||||||
private readonly PolicySnapshotService $snapshotService,
|
private readonly PolicySnapshotService $snapshotService,
|
||||||
private readonly AssignmentFetcher $assignmentFetcher,
|
private readonly AssignmentFetcher $assignmentFetcher,
|
||||||
private readonly GroupResolver $groupResolver,
|
private readonly GroupResolver $groupResolver,
|
||||||
|
private readonly AssignmentFilterResolver $assignmentFilterResolver,
|
||||||
private readonly ScopeTagResolver $scopeTagResolver,
|
private readonly ScopeTagResolver $scopeTagResolver,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@ -92,7 +94,7 @@ public function captureFromGraph(
|
|||||||
$rawAssignments = $this->assignmentFetcher->fetch($tenantIdentifier, $policy->external_id, $graphOptions);
|
$rawAssignments = $this->assignmentFetcher->fetch($tenantIdentifier, $policy->external_id, $graphOptions);
|
||||||
|
|
||||||
if (! empty($rawAssignments)) {
|
if (! empty($rawAssignments)) {
|
||||||
$assignments = $rawAssignments;
|
$resolvedGroups = [];
|
||||||
|
|
||||||
// Resolve groups
|
// Resolve groups
|
||||||
$groupIds = collect($rawAssignments)
|
$groupIds = collect($rawAssignments)
|
||||||
@ -102,11 +104,27 @@ public function captureFromGraph(
|
|||||||
->values()
|
->values()
|
||||||
->toArray();
|
->toArray();
|
||||||
|
|
||||||
$resolvedGroups = $this->groupResolver->resolveGroupIds($groupIds, $tenantIdentifier, $graphOptions);
|
if (! empty($groupIds)) {
|
||||||
|
$resolvedGroups = $this->groupResolver->resolveGroupIds($groupIds, $tenantIdentifier, $graphOptions);
|
||||||
|
}
|
||||||
|
|
||||||
$assignmentMetadata['has_orphaned_assignments'] = collect($resolvedGroups)
|
$assignmentMetadata['has_orphaned_assignments'] = collect($resolvedGroups)
|
||||||
->contains(fn (array $group) => $group['orphaned'] ?? false);
|
->contains(fn (array $group) => $group['orphaned'] ?? false);
|
||||||
$assignmentMetadata['assignments_count'] = count($rawAssignments);
|
$assignmentMetadata['assignments_count'] = count($rawAssignments);
|
||||||
|
|
||||||
|
$filterIds = collect($rawAssignments)
|
||||||
|
->pluck('target.deviceAndAppManagementAssignmentFilterId')
|
||||||
|
->filter()
|
||||||
|
->unique()
|
||||||
|
->values()
|
||||||
|
->all();
|
||||||
|
|
||||||
|
$filters = $this->assignmentFilterResolver->resolve($filterIds, $tenant);
|
||||||
|
$filterNames = collect($filters)
|
||||||
|
->pluck('displayName', 'id')
|
||||||
|
->all();
|
||||||
|
|
||||||
|
$assignments = $this->enrichAssignments($rawAssignments, $resolvedGroups, $filterNames);
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
$assignmentMetadata['assignments_fetch_failed'] = true;
|
$assignmentMetadata['assignments_fetch_failed'] = true;
|
||||||
@ -135,6 +153,34 @@ public function captureFromGraph(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<int, array<string, mixed>> $assignments
|
||||||
|
* @param array<string, array{id:string,displayName:?string,orphaned:bool}> $groups
|
||||||
|
* @param array<string, string> $filterNames
|
||||||
|
* @return array<int, array<string, mixed>>
|
||||||
|
*/
|
||||||
|
private function enrichAssignments(array $assignments, array $groups, array $filterNames): array
|
||||||
|
{
|
||||||
|
return array_map(function (array $assignment) use ($groups, $filterNames): array {
|
||||||
|
$target = $assignment['target'] ?? [];
|
||||||
|
$groupId = $target['groupId'] ?? null;
|
||||||
|
|
||||||
|
if ($groupId && isset($groups[$groupId])) {
|
||||||
|
$target['group_display_name'] = $groups[$groupId]['displayName'] ?? null;
|
||||||
|
$target['group_orphaned'] = $groups[$groupId]['orphaned'] ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$filterId = $target['deviceAndAppManagementAssignmentFilterId'] ?? null;
|
||||||
|
if ($filterId && isset($filterNames[$filterId])) {
|
||||||
|
$target['assignment_filter_name'] = $filterNames[$filterId];
|
||||||
|
}
|
||||||
|
|
||||||
|
$assignment['target'] = $target;
|
||||||
|
|
||||||
|
return $assignment;
|
||||||
|
}, $assignments);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array<int, string> $scopeTagIds
|
* @param array<int, string> $scopeTagIds
|
||||||
* @return array{ids:array<int, string>,names:array<int, string>}
|
* @return array{ids:array<int, string>,names:array<int, string>}
|
||||||
|
|||||||
@ -57,14 +57,19 @@
|
|||||||
$intent = $assignment['intent'] ?? 'apply';
|
$intent = $assignment['intent'] ?? 'apply';
|
||||||
|
|
||||||
$typeName = match($type) {
|
$typeName = match($type) {
|
||||||
'#microsoft.graph.groupAssignmentTarget' => 'Group',
|
'#microsoft.graph.groupAssignmentTarget' => 'Include group',
|
||||||
|
'#microsoft.graph.exclusionGroupAssignmentTarget' => 'Exclude group',
|
||||||
'#microsoft.graph.allLicensedUsersAssignmentTarget' => 'All Users',
|
'#microsoft.graph.allLicensedUsersAssignmentTarget' => 'All Users',
|
||||||
'#microsoft.graph.allDevicesAssignmentTarget' => 'All Devices',
|
'#microsoft.graph.allDevicesAssignmentTarget' => 'All Devices',
|
||||||
default => 'Unknown'
|
default => 'Unknown'
|
||||||
};
|
};
|
||||||
|
|
||||||
$groupId = $target['groupId'] ?? null;
|
$groupId = $target['groupId'] ?? null;
|
||||||
$hasOrphaned = $version->metadata['has_orphaned_assignments'] ?? false;
|
$groupName = $target['group_display_name'] ?? null;
|
||||||
|
$groupOrphaned = $target['group_orphaned'] ?? ($version->metadata['has_orphaned_assignments'] ?? false);
|
||||||
|
$filterId = $target['deviceAndAppManagementAssignmentFilterId'] ?? null;
|
||||||
|
$filterType = $target['deviceAndAppManagementAssignmentFilterType'] ?? 'none';
|
||||||
|
$filterName = $target['assignment_filter_name'] ?? null;
|
||||||
@endphp
|
@endphp
|
||||||
|
|
||||||
<div class="flex items-center gap-2 text-sm">
|
<div class="flex items-center gap-2 text-sm">
|
||||||
@ -73,9 +78,16 @@
|
|||||||
|
|
||||||
@if($groupId)
|
@if($groupId)
|
||||||
<span class="text-gray-600 dark:text-gray-400">:</span>
|
<span class="text-gray-600 dark:text-gray-400">:</span>
|
||||||
@if($hasOrphaned)
|
@if($groupOrphaned)
|
||||||
<span class="text-warning-600 dark:text-warning-400">
|
<span class="text-warning-600 dark:text-warning-400">
|
||||||
⚠️ Unknown Group (ID: {{ $groupId }})
|
⚠️ Unknown group (ID: {{ $groupId }})
|
||||||
|
</span>
|
||||||
|
@elseif($groupName)
|
||||||
|
<span class="text-gray-700 dark:text-gray-300">
|
||||||
|
{{ $groupName }}
|
||||||
|
</span>
|
||||||
|
<span class="text-xs text-gray-500 dark:text-gray-500">
|
||||||
|
({{ $groupId }})
|
||||||
</span>
|
</span>
|
||||||
@else
|
@else
|
||||||
<span class="text-gray-700 dark:text-gray-300">
|
<span class="text-gray-700 dark:text-gray-300">
|
||||||
@ -83,6 +95,12 @@
|
|||||||
</span>
|
</span>
|
||||||
@endif
|
@endif
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
|
@if($filterId && $filterType !== 'none')
|
||||||
|
<span class="text-xs text-gray-500 dark:text-gray-500">
|
||||||
|
Filter ({{ $filterType }}): {{ $filterName ?? $filterId }}
|
||||||
|
</span>
|
||||||
|
@endif
|
||||||
|
|
||||||
<span class="ml-auto text-xs text-gray-500 dark:text-gray-500">({{ $intent }})</span>
|
<span class="ml-auto text-xs text-gray-500 dark:text-gray-500">({{ $intent }})</span>
|
||||||
</div>
|
</div>
|
||||||
@ -115,4 +133,3 @@
|
|||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@ -39,6 +39,19 @@
|
|||||||
'target' => [
|
'target' => [
|
||||||
'@odata.type' => '#microsoft.graph.groupAssignmentTarget',
|
'@odata.type' => '#microsoft.graph.groupAssignmentTarget',
|
||||||
'groupId' => 'group-123',
|
'groupId' => 'group-123',
|
||||||
|
'assignment_filter_name' => 'Targeted Devices',
|
||||||
|
'deviceAndAppManagementAssignmentFilterId' => 'filter-1',
|
||||||
|
'deviceAndAppManagementAssignmentFilterType' => 'include',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'id' => 'assignment-2',
|
||||||
|
'intent' => 'apply',
|
||||||
|
'target' => [
|
||||||
|
'@odata.type' => '#microsoft.graph.exclusionGroupAssignmentTarget',
|
||||||
|
'groupId' => 'group-456',
|
||||||
|
'deviceAndAppManagementAssignmentFilterId' => null,
|
||||||
|
'deviceAndAppManagementAssignmentFilterType' => 'none',
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
@ -54,7 +67,10 @@
|
|||||||
|
|
||||||
$response->assertOk();
|
$response->assertOk();
|
||||||
$response->assertSeeLivewire('policy-version-assignments-widget');
|
$response->assertSeeLivewire('policy-version-assignments-widget');
|
||||||
$response->assertSee('1 assignment(s)');
|
$response->assertSee('2 assignment(s)');
|
||||||
|
$response->assertSee('Include group');
|
||||||
|
$response->assertSee('Exclude group');
|
||||||
|
$response->assertSee('Filter (include): Targeted Devices');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('displays empty state when version has no assignments', function () {
|
it('displays empty state when version has no assignments', function () {
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
use App\Models\Policy;
|
use App\Models\Policy;
|
||||||
use App\Models\Tenant;
|
use App\Models\Tenant;
|
||||||
use App\Services\Graph\AssignmentFetcher;
|
use App\Services\Graph\AssignmentFetcher;
|
||||||
|
use App\Services\Graph\AssignmentFilterResolver;
|
||||||
use App\Services\Graph\GroupResolver;
|
use App\Services\Graph\GroupResolver;
|
||||||
use App\Services\Graph\ScopeTagResolver;
|
use App\Services\Graph\ScopeTagResolver;
|
||||||
use App\Services\Intune\PolicySnapshotService;
|
use App\Services\Intune\PolicySnapshotService;
|
||||||
@ -47,6 +48,8 @@
|
|||||||
'target' => [
|
'target' => [
|
||||||
'@odata.type' => '#microsoft.graph.groupAssignmentTarget',
|
'@odata.type' => '#microsoft.graph.groupAssignmentTarget',
|
||||||
'groupId' => 'group-123',
|
'groupId' => 'group-123',
|
||||||
|
'deviceAndAppManagementAssignmentFilterId' => 'filter-123',
|
||||||
|
'deviceAndAppManagementAssignmentFilterType' => 'include',
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
@ -64,6 +67,14 @@
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$this->mock(AssignmentFilterResolver::class, function ($mock) {
|
||||||
|
$mock->shouldReceive('resolve')
|
||||||
|
->once()
|
||||||
|
->andReturn([
|
||||||
|
['id' => 'filter-123', 'displayName' => 'Targeted Devices'],
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
$versionService = app(VersionService::class);
|
$versionService = app(VersionService::class);
|
||||||
$version = $versionService->captureFromGraph(
|
$version = $versionService->captureFromGraph(
|
||||||
$this->tenant,
|
$this->tenant,
|
||||||
@ -82,6 +93,9 @@
|
|||||||
'ids' => ['0'],
|
'ids' => ['0'],
|
||||||
'names' => ['Default'],
|
'names' => ['Default'],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
expect($version->assignments[0]['target']['group_display_name'])->toBe('Test Group');
|
||||||
|
expect($version->assignments[0]['target']['assignment_filter_name'])->toBe('Targeted Devices');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('captures policy version without assignments when none exist', function () {
|
it('captures policy version without assignments when none exist', function () {
|
||||||
|
|||||||
65
tests/Unit/AssignmentFilterResolverTest.php
Normal file
65
tests/Unit/AssignmentFilterResolverTest.php
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Services\Graph\AssignmentFilterResolver;
|
||||||
|
use App\Services\Graph\GraphResponse;
|
||||||
|
use App\Services\Graph\MicrosoftGraphClient;
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
uses(TestCase::class, RefreshDatabase::class);
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
Cache::flush();
|
||||||
|
$this->graphClient = Mockery::mock(MicrosoftGraphClient::class);
|
||||||
|
$this->resolver = new AssignmentFilterResolver($this->graphClient);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('resolves assignment filters by id', function () {
|
||||||
|
$filters = [
|
||||||
|
['id' => 'filter-1', 'displayName' => 'Targeted Devices'],
|
||||||
|
['id' => 'filter-2', 'displayName' => 'Excluded Devices'],
|
||||||
|
];
|
||||||
|
|
||||||
|
$response = new GraphResponse(
|
||||||
|
success: true,
|
||||||
|
data: ['value' => $filters]
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->graphClient
|
||||||
|
->shouldReceive('request')
|
||||||
|
->once()
|
||||||
|
->with('GET', '/deviceManagement/assignmentFilters', [
|
||||||
|
'query' => [
|
||||||
|
'$select' => 'id,displayName',
|
||||||
|
],
|
||||||
|
])
|
||||||
|
->andReturn($response);
|
||||||
|
|
||||||
|
$result = $this->resolver->resolve(['filter-1']);
|
||||||
|
|
||||||
|
expect($result)->toHaveCount(1)
|
||||||
|
->and($result[0]['id'])->toBe('filter-1')
|
||||||
|
->and($result[0]['displayName'])->toBe('Targeted Devices');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('uses cache for repeated lookups', function () {
|
||||||
|
$filters = [
|
||||||
|
['id' => 'filter-1', 'displayName' => 'Targeted Devices'],
|
||||||
|
];
|
||||||
|
|
||||||
|
$response = new GraphResponse(
|
||||||
|
success: true,
|
||||||
|
data: ['value' => $filters]
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->graphClient
|
||||||
|
->shouldReceive('request')
|
||||||
|
->once()
|
||||||
|
->andReturn($response);
|
||||||
|
|
||||||
|
$result1 = $this->resolver->resolve(['filter-1']);
|
||||||
|
$result2 = $this->resolver->resolve(['filter-1']);
|
||||||
|
|
||||||
|
expect($result1)->toBe($result2);
|
||||||
|
});
|
||||||
Loading…
Reference in New Issue
Block a user