feat(17-02): wire ISharePointGroupResolver into PermissionsViewModel export flow

- Add _groupResolver field (ISharePointGroupResolver?) with constructor injection
- ISharePointGroupResolver added as optional last parameter in main constructor
- ExportHtmlAsync resolves SharePoint group names before calling WriteAsync
- Gracefully handles resolution failure with LogWarning, exports without expansion
- Both WriteAsync call sites pass groupMembers dict (standard and simplified paths)
This commit is contained in:
Dev
2026-04-09 13:10:31 +02:00
parent 07ed6e2515
commit aab3aee3df

View File

@@ -27,6 +27,7 @@ public partial class PermissionsViewModel : FeatureViewModelBase
private readonly CsvExportService? _csvExportService; private readonly CsvExportService? _csvExportService;
private readonly HtmlExportService? _htmlExportService; private readonly HtmlExportService? _htmlExportService;
private readonly IBrandingService? _brandingService; private readonly IBrandingService? _brandingService;
private readonly ISharePointGroupResolver? _groupResolver;
private readonly ILogger<FeatureViewModelBase> _logger; private readonly ILogger<FeatureViewModelBase> _logger;
// ── Observable properties ─────────────────────────────────────────────── // ── Observable properties ───────────────────────────────────────────────
@@ -134,7 +135,8 @@ public partial class PermissionsViewModel : FeatureViewModelBase
CsvExportService csvExportService, CsvExportService csvExportService,
HtmlExportService htmlExportService, HtmlExportService htmlExportService,
IBrandingService brandingService, IBrandingService brandingService,
ILogger<FeatureViewModelBase> logger) ILogger<FeatureViewModelBase> logger,
ISharePointGroupResolver? groupResolver = null)
: base(logger) : base(logger)
{ {
_permissionsService = permissionsService; _permissionsService = permissionsService;
@@ -143,6 +145,7 @@ public partial class PermissionsViewModel : FeatureViewModelBase
_csvExportService = csvExportService; _csvExportService = csvExportService;
_htmlExportService = htmlExportService; _htmlExportService = htmlExportService;
_brandingService = brandingService; _brandingService = brandingService;
_groupResolver = groupResolver;
_logger = logger; _logger = logger;
ExportCsvCommand = new AsyncRelayCommand(ExportCsvAsync, CanExport); ExportCsvCommand = new AsyncRelayCommand(ExportCsvAsync, CanExport);
@@ -330,10 +333,37 @@ public partial class PermissionsViewModel : FeatureViewModelBase
branding = new ReportBranding(mspLogo, clientLogo); branding = new ReportBranding(mspLogo, clientLogo);
} }
IReadOnlyDictionary<string, IReadOnlyList<ResolvedMember>>? groupMembers = null;
if (_groupResolver != null && Results.Count > 0)
{
var groupNames = Results
.Where(r => r.PrincipalType == "SharePointGroup")
.SelectMany(r => r.Users.Split(';', StringSplitOptions.RemoveEmptyEntries))
.Select(n => n.Trim())
.Where(n => n.Length > 0)
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToList();
if (groupNames.Count > 0 && _currentProfile != null)
{
try
{
var ctx = await _sessionManager.GetOrCreateContextAsync(
_currentProfile, CancellationToken.None);
groupMembers = await _groupResolver.ResolveGroupsAsync(
ctx, _currentProfile.ClientId, groupNames, CancellationToken.None);
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Group resolution failed — exporting without member expansion.");
}
}
}
if (IsSimplifiedMode && SimplifiedResults.Count > 0) if (IsSimplifiedMode && SimplifiedResults.Count > 0)
await _htmlExportService.WriteAsync(SimplifiedResults.ToList(), dialog.FileName, CancellationToken.None, branding); await _htmlExportService.WriteAsync(SimplifiedResults.ToList(), dialog.FileName, CancellationToken.None, branding, groupMembers);
else else
await _htmlExportService.WriteAsync(Results, dialog.FileName, CancellationToken.None, branding); await _htmlExportService.WriteAsync(Results, dialog.FileName, CancellationToken.None, branding, groupMembers);
OpenFile(dialog.FileName); OpenFile(dialog.FileName);
} }
catch (Exception ex) catch (Exception ex)