From 440b2474e9a0b1977226a35efc39958f04dca738 Mon Sep 17 00:00:00 2001 From: Dev Date: Thu, 9 Apr 2026 11:41:26 +0200 Subject: [PATCH] feat(15-01): add PermissionConsolidator static helper - MakeKey builds pipe-delimited case-insensitive key from UserLogin+PermissionLevel+AccessType+GrantedThrough - Consolidate groups UserAccessEntry list by key, merges into ConsolidatedPermissionEntry rows - Empty input short-circuits to Array.Empty - Output ordered by UserLogin then PermissionLevel for deterministic results --- .../Core/Helpers/PermissionConsolidator.cs | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 SharepointToolbox/Core/Helpers/PermissionConsolidator.cs diff --git a/SharepointToolbox/Core/Helpers/PermissionConsolidator.cs b/SharepointToolbox/Core/Helpers/PermissionConsolidator.cs new file mode 100644 index 0000000..6322b8a --- /dev/null +++ b/SharepointToolbox/Core/Helpers/PermissionConsolidator.cs @@ -0,0 +1,59 @@ +using SharepointToolbox.Core.Models; + +namespace SharepointToolbox.Core.Helpers; + +/// +/// 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. +/// +public static class PermissionConsolidator +{ + /// + /// Builds a pipe-delimited, case-insensitive composite key from the four key fields. + /// + internal static string MakeKey(UserAccessEntry entry) + { + return string.Join("|", + entry.UserLogin.ToLowerInvariant(), + entry.PermissionLevel.ToLowerInvariant(), + entry.AccessType.ToString(), + entry.GrantedThrough.ToLowerInvariant()); + } + + /// + /// 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. + /// + public static IReadOnlyList Consolidate( + IReadOnlyList entries) + { + if (entries.Count == 0) + return Array.Empty(); + + 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(); + } +}