236 lines
9.0 KiB
Markdown
236 lines
9.0 KiB
Markdown
---
|
|
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"
|
|
---
|
|
|
|
<objective>
|
|
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.
|
|
</objective>
|
|
|
|
<execution_context>
|
|
@C:/Users/dev/.claude/get-shit-done/workflows/execute-plan.md
|
|
@C:/Users/dev/.claude/get-shit-done/templates/summary.md
|
|
</execution_context>
|
|
|
|
<context>
|
|
@.planning/PROJECT.md
|
|
@.planning/ROADMAP.md
|
|
@.planning/phases/13-user-directory-viewmodel/13-RESEARCH.md
|
|
|
|
<interfaces>
|
|
<!-- Current GraphDirectoryUser model -->
|
|
From SharepointToolbox/Core/Models/GraphDirectoryUser.cs:
|
|
```csharp
|
|
public record GraphDirectoryUser(
|
|
string DisplayName,
|
|
string UserPrincipalName,
|
|
string? Mail,
|
|
string? Department,
|
|
string? JobTitle);
|
|
```
|
|
|
|
<!-- Current interface -->
|
|
From SharepointToolbox/Services/IGraphUserDirectoryService.cs:
|
|
```csharp
|
|
public interface IGraphUserDirectoryService
|
|
{
|
|
Task<IReadOnlyList<GraphDirectoryUser>> GetUsersAsync(
|
|
string clientId,
|
|
IProgress<int>? progress = null,
|
|
CancellationToken ct = default);
|
|
}
|
|
```
|
|
|
|
<!-- Current implementation (key parts) -->
|
|
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);
|
|
```
|
|
</interfaces>
|
|
</context>
|
|
|
|
<tasks>
|
|
|
|
<task type="auto" tdd="true">
|
|
<name>Task 1: Add UserType to GraphDirectoryUser</name>
|
|
<files>
|
|
SharepointToolbox/Core/Models/GraphDirectoryUser.cs
|
|
</files>
|
|
<behavior>
|
|
- GraphDirectoryUser record has 6 positional parameters: DisplayName, UserPrincipalName, Mail, Department, JobTitle, UserType
|
|
- UserType is nullable string (string?) — appended as last parameter for backward compat
|
|
</behavior>
|
|
<action>
|
|
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.
|
|
</action>
|
|
<verify>
|
|
<automated>dotnet build --no-restore -warnaserror</automated>
|
|
</verify>
|
|
<done>GraphDirectoryUser has UserType property. All construction sites updated. Build passes.</done>
|
|
</task>
|
|
|
|
<task type="auto" tdd="true">
|
|
<name>Task 2: Add includeGuests parameter to interface and implementation</name>
|
|
<files>
|
|
SharepointToolbox/Services/IGraphUserDirectoryService.cs,
|
|
SharepointToolbox/Services/GraphUserDirectoryService.cs
|
|
</files>
|
|
<behavior>
|
|
- 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
|
|
</behavior>
|
|
<action>
|
|
1. Update `IGraphUserDirectoryService.cs`:
|
|
```csharp
|
|
Task<IReadOnlyList<GraphDirectoryUser>> GetUsersAsync(
|
|
string clientId,
|
|
bool includeGuests = false,
|
|
IProgress<int>? 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);
|
|
```
|
|
</action>
|
|
<verify>
|
|
<automated>dotnet build --no-restore -warnaserror</automated>
|
|
</verify>
|
|
<done>Interface and implementation updated. Default parameter preserves backward compat. Build passes.</done>
|
|
</task>
|
|
|
|
<task type="auto" tdd="true">
|
|
<name>Task 3: Update tests</name>
|
|
<files>
|
|
SharepointToolbox.Tests/Services/GraphUserDirectoryServiceTests.cs
|
|
</files>
|
|
<behavior>
|
|
- 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
|
|
</behavior>
|
|
<action>
|
|
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
|
|
</action>
|
|
<verify>
|
|
<automated>dotnet test SharepointToolbox.Tests --filter "FullyQualifiedName~GraphUserDirectoryService" --no-build -q</automated>
|
|
</verify>
|
|
<done>All MapUser tests pass including UserType coverage.</done>
|
|
</task>
|
|
|
|
</tasks>
|
|
|
|
<verification>
|
|
```bash
|
|
dotnet build --no-restore -warnaserror
|
|
dotnet test SharepointToolbox.Tests --filter "FullyQualifiedName~GraphUserDirectoryService" --no-build -q
|
|
```
|
|
Both must pass with zero failures.
|
|
</verification>
|
|
|
|
<success_criteria>
|
|
- 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
|
|
</success_criteria>
|
|
|
|
<output>
|
|
After completion, create `.planning/phases/13-user-directory-viewmodel/13-01-SUMMARY.md`
|
|
</output>
|