--- phase: 13-user-directory-viewmodel plan: 01 type: execute wave: 1 depends_on: [] files_modified: - SharepointToolbox/Core/Models/GraphDirectoryUser.cs - SharepointToolbox/Services/IGraphUserDirectoryService.cs - SharepointToolbox/Services/GraphUserDirectoryService.cs - SharepointToolbox.Tests/Services/GraphUserDirectoryServiceTests.cs autonomous: true requirements: - UDIR-03 must_haves: truths: - "GraphDirectoryUser record includes a UserType property (string?) alongside the existing five properties" - "GraphUserDirectoryService.MapUser populates UserType from the Graph User object" - "IGraphUserDirectoryService.GetUsersAsync accepts an optional bool includeGuests parameter defaulting to false" - "When includeGuests is false, the Graph filter remains 'accountEnabled eq true and userType eq Member' (backward compatible)" - "When includeGuests is true, the Graph filter is 'accountEnabled eq true' (no userType restriction) and userType is in the select set" - "Existing tests continue to pass with no changes required (default parameter preserves old behavior)" artifacts: - path: "SharepointToolbox/Core/Models/GraphDirectoryUser.cs" provides: "Directory user record with UserType for client-side member/guest filtering" contains: "UserType" - path: "SharepointToolbox/Services/IGraphUserDirectoryService.cs" provides: "Interface with includeGuests parameter" contains: "includeGuests" - path: "SharepointToolbox/Services/GraphUserDirectoryService.cs" provides: "Implementation branching filter based on includeGuests" contains: "includeGuests" key_links: - from: "SharepointToolbox/Services/GraphUserDirectoryService.cs" to: "SharepointToolbox/Core/Models/GraphDirectoryUser.cs" via: "MapUser" pattern: "UserType" --- Extend GraphDirectoryUser with a UserType property and add an includeGuests parameter to GraphUserDirectoryService so that Phase 13-02 can load all users and filter members/guests in-memory. Purpose: SC3 requires "Members only / Include guests" toggle that filters in-memory without a new Graph request. The service must fetch all users (members + guests) when requested, and the model must carry UserType for client-side filtering. Output: Updated model, interface, implementation, and tests. @C:/Users/dev/.claude/get-shit-done/workflows/execute-plan.md @C:/Users/dev/.claude/get-shit-done/templates/summary.md @.planning/PROJECT.md @.planning/ROADMAP.md @.planning/phases/13-user-directory-viewmodel/13-RESEARCH.md From SharepointToolbox/Core/Models/GraphDirectoryUser.cs: ```csharp public record GraphDirectoryUser( string DisplayName, string UserPrincipalName, string? Mail, string? Department, string? JobTitle); ``` From SharepointToolbox/Services/IGraphUserDirectoryService.cs: ```csharp public interface IGraphUserDirectoryService { Task> GetUsersAsync( string clientId, IProgress? progress = null, CancellationToken ct = default); } ``` From SharepointToolbox/Services/GraphUserDirectoryService.cs: ```csharp config.QueryParameters.Filter = "accountEnabled eq true and userType eq 'Member'"; config.QueryParameters.Select = new[] { "displayName", "userPrincipalName", "mail", "department", "jobTitle" }; internal static GraphDirectoryUser MapUser(User user) => new( DisplayName: user.DisplayName ?? user.UserPrincipalName ?? string.Empty, UserPrincipalName: user.UserPrincipalName ?? string.Empty, Mail: user.Mail, Department: user.Department, JobTitle: user.JobTitle); ``` Task 1: Add UserType to GraphDirectoryUser SharepointToolbox/Core/Models/GraphDirectoryUser.cs - GraphDirectoryUser record has 6 positional parameters: DisplayName, UserPrincipalName, Mail, Department, JobTitle, UserType - UserType is nullable string (string?) — appended as last parameter for backward compat 1. Edit `SharepointToolbox/Core/Models/GraphDirectoryUser.cs`: Add `string? UserType` as the last parameter: ```csharp public record GraphDirectoryUser( string DisplayName, string UserPrincipalName, string? Mail, string? Department, string? JobTitle, string? UserType); ``` 2. Check for any existing code that constructs GraphDirectoryUser (MapUser, tests) and add the UserType parameter. Search for `new GraphDirectoryUser(` and `new(` in test files to find all construction sites. dotnet build --no-restore -warnaserror GraphDirectoryUser has UserType property. All construction sites updated. Build passes. Task 2: Add includeGuests parameter to interface and implementation SharepointToolbox/Services/IGraphUserDirectoryService.cs, SharepointToolbox/Services/GraphUserDirectoryService.cs - IGraphUserDirectoryService.GetUsersAsync has a new `bool includeGuests = false` parameter - When includeGuests=false: filter is "accountEnabled eq true and userType eq 'Member'" (unchanged) - When includeGuests=true: filter is "accountEnabled eq true" (fetches members + guests) - "userType" is always in the select set (needed for MapUser) - MapUser includes user.UserType in the mapping 1. Update `IGraphUserDirectoryService.cs`: ```csharp Task> GetUsersAsync( string clientId, bool includeGuests = false, IProgress? progress = null, CancellationToken ct = default); ``` 2. Update `GraphUserDirectoryService.cs`: - Update method signature to match interface - Add `userType` to Select array - Branch filter based on includeGuests: ```csharp config.QueryParameters.Filter = includeGuests ? "accountEnabled eq true" : "accountEnabled eq true and userType eq 'Member'"; config.QueryParameters.Select = new[] { "displayName", "userPrincipalName", "mail", "department", "jobTitle", "userType" }; ``` - Update MapUser: ```csharp internal static GraphDirectoryUser MapUser(User user) => new( DisplayName: user.DisplayName ?? user.UserPrincipalName ?? string.Empty, UserPrincipalName: user.UserPrincipalName ?? string.Empty, Mail: user.Mail, Department: user.Department, JobTitle: user.JobTitle, UserType: user.UserType); ``` dotnet build --no-restore -warnaserror Interface and implementation updated. Default parameter preserves backward compat. Build passes. Task 3: Update tests SharepointToolbox.Tests/Services/GraphUserDirectoryServiceTests.cs - Existing MapUser tests pass with UserType parameter added - New test: MapUser populates UserType from User.UserType - New test: MapUser returns null UserType when User.UserType is null 1. Read `SharepointToolbox.Tests/Services/GraphUserDirectoryServiceTests.cs` 2. Update any existing `MapUser` test assertions to include the UserType field 3. Add test: MapUser_PopulatesUserType — set User.UserType = "Member", verify GraphDirectoryUser.UserType == "Member" 4. Add test: MapUser_NullUserType — set User.UserType = null, verify GraphDirectoryUser.UserType is null 5. Run tests dotnet test SharepointToolbox.Tests --filter "FullyQualifiedName~GraphUserDirectoryService" --no-build -q All MapUser tests pass including UserType coverage. ```bash dotnet build --no-restore -warnaserror dotnet test SharepointToolbox.Tests --filter "FullyQualifiedName~GraphUserDirectoryService" --no-build -q ``` Both must pass with zero failures. - GraphDirectoryUser has UserType (string?) as last positional parameter - IGraphUserDirectoryService.GetUsersAsync has bool includeGuests = false parameter - When includeGuests=false, filter unchanged (backward compatible) - When includeGuests=true, filter omits userType restriction - MapUser populates UserType from Graph User object - userType always in select set - All tests pass After completion, create `.planning/phases/13-user-directory-viewmodel/13-01-SUMMARY.md`