Two plans for Phase 15: models + consolidator service (wave 1), unit tests + build verification (wave 2). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
9.1 KiB
phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, must_haves
| phase | plan | type | wave | depends_on | files_modified | autonomous | requirements | must_haves | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 15-consolidation-data-model | 01 | execute | 1 |
|
true |
|
|
Purpose: Establish the data shape (LocationInfo, ConsolidatedPermissionEntry) and pure-function merge logic (PermissionConsolidator) so that Phase 16 can wire them into the export pipeline. Zero API calls, zero UI — just models and a static helper.
Output: Three production files — two model records and one static consolidation service.
<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>
@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/15-consolidation-data-model/15-CONTEXT.md @.planning/phases/15-consolidation-data-model/15-RESEARCH.md ```csharp namespace SharepointToolbox.Core.Models;public enum AccessType { Direct, Group, Inherited }
public record UserAccessEntry( string UserDisplayName, string UserLogin, string SiteUrl, string SiteTitle, string ObjectType, string ObjectTitle, string ObjectUrl, string PermissionLevel, AccessType AccessType, string GrantedThrough, bool IsHighPrivilege, bool IsExternalUser );
</interfaces>
</context>
<tasks>
<task type="auto">
<name>Task 1: Create LocationInfo and ConsolidatedPermissionEntry model records</name>
<files>SharepointToolbox/Core/Models/LocationInfo.cs, SharepointToolbox/Core/Models/ConsolidatedPermissionEntry.cs</files>
<action>
Create two new C# positional record files in `Core/Models/`.
**LocationInfo.cs** — namespace `SharepointToolbox.Core.Models`:
```csharp
public record LocationInfo(
string SiteUrl,
string SiteTitle,
string ObjectTitle,
string ObjectUrl,
string ObjectType
);
Lightweight record holding the five location-related fields extracted from UserAccessEntry when rows are merged.
ConsolidatedPermissionEntry.cs — namespace SharepointToolbox.Core.Models:
public record ConsolidatedPermissionEntry(
string UserDisplayName,
string UserLogin,
string PermissionLevel,
AccessType AccessType,
string GrantedThrough,
bool IsHighPrivilege,
bool IsExternalUser,
IReadOnlyList<LocationInfo> Locations
)
{
public int LocationCount => Locations.Count;
}
- Holds the four key fields (UserLogin, PermissionLevel, AccessType, GrantedThrough) plus carried-forward fields (UserDisplayName, IsHighPrivilege, IsExternalUser).
Locationsis anIReadOnlyList<LocationInfo>containing all merged locations.LocationCountis a computed convenience property.- Do NOT add any methods, constructors, or logic beyond the record definition and LocationCount property. cd C:/Users/dev/Documents/projets/Sharepoint && dotnet build SharepointToolbox/SharepointToolbox.csproj --no-restore -v q 2>&1 | tail -5 Both record files exist, compile without errors, and are in the SharepointToolbox.Core.Models namespace. ConsolidatedPermissionEntry.LocationCount returns Locations.Count.
Follow the existing DuplicatesService.MakeKey() pattern for composite key generation.
using SharepointToolbox.Core.Models;
namespace SharepointToolbox.Core.Helpers;
/// <summary>
/// Merges a flat list of UserAccessEntry rows into consolidated entries
/// where rows with identical (UserLogin, PermissionLevel, AccessType, GrantedThrough)
/// are grouped into a single row with multiple locations.
/// </summary>
public static class PermissionConsolidator
{
/// <summary>
/// Builds a pipe-delimited, case-insensitive composite key from the four key fields.
/// </summary>
internal static string MakeKey(UserAccessEntry entry)
{
return string.Join("|",
entry.UserLogin.ToLowerInvariant(),
entry.PermissionLevel.ToLowerInvariant(),
entry.AccessType.ToString(),
entry.GrantedThrough.ToLowerInvariant());
}
/// <summary>
/// Groups entries by composite key and returns consolidated rows.
/// Each group's first entry provides UserDisplayName, IsHighPrivilege, IsExternalUser.
/// All entries in a group contribute a LocationInfo to the Locations list.
/// Results are ordered by UserLogin then PermissionLevel.
/// </summary>
public static IReadOnlyList<ConsolidatedPermissionEntry> Consolidate(
IReadOnlyList<UserAccessEntry> entries)
{
if (entries.Count == 0)
return Array.Empty<ConsolidatedPermissionEntry>();
return entries
.GroupBy(e => MakeKey(e))
.Select(g =>
{
var first = g.First();
var locations = g.Select(e => new LocationInfo(
e.SiteUrl, e.SiteTitle, e.ObjectTitle, e.ObjectUrl, e.ObjectType
)).ToList();
return new ConsolidatedPermissionEntry(
first.UserDisplayName,
first.UserLogin,
first.PermissionLevel,
first.AccessType,
first.GrantedThrough,
first.IsHighPrivilege,
first.IsExternalUser,
locations);
})
.OrderBy(c => c.UserLogin)
.ThenBy(c => c.PermissionLevel)
.ToList();
}
}
Key implementation details:
MakeKeyisinternalso tests can access it via[InternalsVisibleTo]or by testing throughConsolidate.- Use
.ToLowerInvariant()on UserLogin, PermissionLevel, GrantedThrough (string key fields). AccessType is an enum — use.ToString()(case-stable). - Empty input short-circuits to
Array.Empty<>(). - LINQ GroupBy + Select pattern — no mutable dictionaries.
- OrderBy UserLogin then PermissionLevel for deterministic output. cd C:/Users/dev/Documents/projets/Sharepoint && dotnet build SharepointToolbox/SharepointToolbox.csproj --no-restore -v q 2>&1 | tail -5 PermissionConsolidator.cs compiles. Consolidate method accepts IReadOnlyList of UserAccessEntry, returns IReadOnlyList of ConsolidatedPermissionEntry. MakeKey produces pipe-delimited lowercase composite key.
<success_criteria>
- LocationInfo.cs and ConsolidatedPermissionEntry.cs exist in Core/Models/ with correct record signatures
- PermissionConsolidator.cs exists in Core/Helpers/ with Consolidate and MakeKey methods
- All three files compile as part of the SharepointToolbox project
- No changes to any existing files </success_criteria>