- ResolvedMember record in Core/Models with DisplayName and Login - ISharePointGroupResolver interface with ResolveGroupsAsync contract - SharePointGroupResolver: CSOM group user loading + Graph transitive AAD resolution - Internal static helpers IsAadGroup, ExtractAadGroupId, StripClaims (all green unit tests) - Graceful error handling: exceptions return empty list per group, never throw - OrdinalIgnoreCase result dict; lazy Graph client creation on first AAD group
38 lines
1.9 KiB
C#
38 lines
1.9 KiB
C#
using Microsoft.SharePoint.Client;
|
|
using SharepointToolbox.Core.Models;
|
|
|
|
namespace SharepointToolbox.Services;
|
|
|
|
/// <summary>
|
|
/// Resolves SharePoint group names to their transitive member lists via CSOM + Microsoft Graph.
|
|
/// Used as a pre-render step in HTML report export (Phase 17).
|
|
/// </summary>
|
|
public interface ISharePointGroupResolver
|
|
{
|
|
/// <summary>
|
|
/// Resolves the given SharePoint group names to their transitive leaf-user members.
|
|
///
|
|
/// For each group name:
|
|
/// - CSOM loads the direct members from <see cref="Microsoft.SharePoint.Client.GroupCollection"/>.
|
|
/// - Any members that are nested AAD groups (login prefix <c>c:0t.c|tenant|</c>) are expanded
|
|
/// transitively via the Microsoft Graph <c>groups/{id}/transitiveMembers/microsoft.graph.user</c> endpoint.
|
|
/// - Members are de-duplicated by login (OrdinalIgnoreCase).
|
|
///
|
|
/// Failures (throttling, group not found, Graph errors) are handled gracefully:
|
|
/// the group entry is included in the result with an empty member list rather than throwing.
|
|
/// </summary>
|
|
/// <param name="ctx">SharePoint ClientContext for CSOM group member loading.</param>
|
|
/// <param name="clientId">App registration client ID used by <see cref="SharepointToolbox.Infrastructure.Auth.GraphClientFactory"/>.</param>
|
|
/// <param name="groupNames">List of SharePoint group display names to resolve.</param>
|
|
/// <param name="ct">Cancellation token.</param>
|
|
/// <returns>
|
|
/// A read-only dictionary keyed by group name (OrdinalIgnoreCase) mapping to the list of resolved members.
|
|
/// Never throws — a group with a resolution failure maps to an empty list.
|
|
/// </returns>
|
|
Task<IReadOnlyDictionary<string, IReadOnlyList<ResolvedMember>>> ResolveGroupsAsync(
|
|
ClientContext ctx,
|
|
string clientId,
|
|
IReadOnlyList<string> groupNames,
|
|
CancellationToken ct);
|
|
}
|