74 lines
3.4 KiB
Markdown
74 lines
3.4 KiB
Markdown
# Phase 13 Research: User Directory ViewModel
|
|
|
|
## What Exists
|
|
|
|
### GraphUserDirectoryService (Phase 10)
|
|
- `GetUsersAsync(clientId, progress?, ct)` → `IReadOnlyList<GraphDirectoryUser>`
|
|
- Filter: `accountEnabled eq true and userType eq 'Member'` (members only)
|
|
- Select: displayName, userPrincipalName, mail, department, jobTitle
|
|
- Uses `PageIterator<User, UserCollectionResponse>` for transparent pagination
|
|
- Reports progress via `IProgress<int>` (running count)
|
|
- Honors cancellation in page callback
|
|
|
|
### GraphDirectoryUser Model
|
|
```csharp
|
|
public record GraphDirectoryUser(
|
|
string DisplayName, string UserPrincipalName,
|
|
string? Mail, string? Department, string? JobTitle);
|
|
```
|
|
**GAP**: No `UserType` property — needed for SC3 member/guest in-memory filtering.
|
|
|
|
### UserAccessAuditViewModel (Phase 7)
|
|
- Inherits `FeatureViewModelBase` (IsRunning, StatusMessage, ProgressValue, RunCommand, CancelCommand)
|
|
- People-picker search: `SearchQuery` → debounce → `IGraphUserSearchService.SearchUsersAsync` → `SearchResults`
|
|
- User selection: `SelectedUsers` (ObservableCollection<GraphUserResult>) → `RunOperationAsync` → audit
|
|
- Results: `Results` (ObservableCollection<UserAccessEntry>) + `ResultsView` (ICollectionView with grouping/filtering)
|
|
- Two constructors: full (DI) and test (omits export services)
|
|
- `_currentProfile` tracks active tenant (via TenantSwitchedMessage)
|
|
- `OnTenantSwitched` clears all state
|
|
|
|
### ICollectionView Pattern (existing in same ViewModel)
|
|
```csharp
|
|
var cvs = new CollectionViewSource { Source = Results };
|
|
ResultsView = cvs.View;
|
|
ResultsView.GroupDescriptions.Add(new PropertyGroupDescription(...));
|
|
ResultsView.Filter = FilterPredicate;
|
|
// On filter change: ResultsView.Refresh();
|
|
```
|
|
|
|
### DI Registration
|
|
- `IGraphUserDirectoryService` registered as Transient
|
|
- `UserAccessAuditViewModel` registered as Transient
|
|
- Currently NOT injected into UserAccessAuditViewModel
|
|
|
|
## Gaps to Fill
|
|
|
|
1. **GraphDirectoryUser needs UserType** — add `string? UserType` to record + update MapUser + select
|
|
2. **Service needs guest inclusion** — add `bool includeGuests` parameter; when true, drop userType filter
|
|
3. **ViewModel needs IGraphUserDirectoryService** — add to both constructors
|
|
4. **ViewModel needs browse mode** — mode toggle, directory collection, load command, cancel, filter, sort
|
|
5. **DI registration** — add IGraphUserDirectoryService to UserAccessAuditViewModel constructor resolution
|
|
|
|
## Plan Breakdown
|
|
|
|
1. **13-01** (Wave 1): Extend GraphDirectoryUser + GraphUserDirectoryService
|
|
- Add UserType to model
|
|
- Add userType to select fields
|
|
- Add `includeGuests` parameter (default false for backward compat)
|
|
- Update MapUser
|
|
- Update tests
|
|
|
|
2. **13-02** (Wave 2): UserAccessAuditViewModel directory browse mode
|
|
- Inject IGraphUserDirectoryService
|
|
- Add AuditMode enum (Search/Browse) + IsBrowseMode toggle
|
|
- Add DirectoryUsers collection + DirectoryUsersView (ICollectionView)
|
|
- Add LoadDirectoryCommand with own CTS, progress reporting
|
|
- Add CancelDirectoryLoadCommand
|
|
- Add IncludeGuests toggle + in-memory filter by UserType
|
|
- Add DirectoryFilterText + filter predicate (DisplayName, UPN, Department, JobTitle)
|
|
- Add SortDescription defaults (DisplayName ascending)
|
|
- Add DirectoryLoadStatus string for "Loading... X users" display
|
|
- Update OnTenantSwitched to clear directory state
|
|
- Update DI in App.xaml.cs
|
|
- Comprehensive tests
|