feat(08-04): add simplified export overloads to CsvExportService
- Add BuildCsv(IReadOnlyList<SimplifiedPermissionEntry>) overload with SimplifiedLabels and RiskLevel columns - Add WriteAsync overload for simplified entries - Original PermissionEntry methods unchanged Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -58,6 +58,60 @@ public class CsvExportService
|
|||||||
await File.WriteAllTextAsync(filePath, csv, new UTF8Encoding(encoderShouldEmitUTF8Identifier: true), ct);
|
await File.WriteAllTextAsync(filePath, csv, new UTF8Encoding(encoderShouldEmitUTF8Identifier: true), ct);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Header for simplified CSV export — includes "SimplifiedLabels" and "RiskLevel" columns.
|
||||||
|
/// </summary>
|
||||||
|
private const string SimplifiedHeader =
|
||||||
|
"\"Object\",\"Title\",\"URL\",\"HasUniquePermissions\",\"Users\",\"UserLogins\",\"Type\",\"Permissions\",\"SimplifiedLabels\",\"RiskLevel\",\"GrantedThrough\"";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Builds a CSV string from simplified permission entries.
|
||||||
|
/// Includes SimplifiedLabels and RiskLevel columns after raw Permissions.
|
||||||
|
/// Uses the same merge logic as the standard BuildCsv.
|
||||||
|
/// </summary>
|
||||||
|
public string BuildCsv(IReadOnlyList<SimplifiedPermissionEntry> entries)
|
||||||
|
{
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
sb.AppendLine(SimplifiedHeader);
|
||||||
|
|
||||||
|
var merged = entries
|
||||||
|
.GroupBy(e => (e.Users, e.PermissionLevels, e.GrantedThrough))
|
||||||
|
.Select(g => new
|
||||||
|
{
|
||||||
|
ObjectType = g.First().ObjectType,
|
||||||
|
Title = string.Join(" | ", g.Select(e => e.Title).Distinct()),
|
||||||
|
Url = string.Join(" | ", g.Select(e => e.Url).Distinct()),
|
||||||
|
HasUnique = g.First().HasUniquePermissions,
|
||||||
|
Users = g.Key.Users,
|
||||||
|
UserLogins = g.First().UserLogins,
|
||||||
|
PrincipalType = g.First().PrincipalType,
|
||||||
|
Permissions = g.Key.PermissionLevels,
|
||||||
|
SimplifiedLabels = g.First().SimplifiedLabels,
|
||||||
|
RiskLevel = g.First().RiskLevel.ToString(),
|
||||||
|
GrantedThrough = g.Key.GrantedThrough
|
||||||
|
});
|
||||||
|
|
||||||
|
foreach (var row in merged)
|
||||||
|
sb.AppendLine(string.Join(",", new[]
|
||||||
|
{
|
||||||
|
Csv(row.ObjectType), Csv(row.Title), Csv(row.Url),
|
||||||
|
Csv(row.HasUnique.ToString()), Csv(row.Users), Csv(row.UserLogins),
|
||||||
|
Csv(row.PrincipalType), Csv(row.Permissions), Csv(row.SimplifiedLabels),
|
||||||
|
Csv(row.RiskLevel), Csv(row.GrantedThrough)
|
||||||
|
}));
|
||||||
|
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes simplified CSV to the specified file path.
|
||||||
|
/// </summary>
|
||||||
|
public async Task WriteAsync(IReadOnlyList<SimplifiedPermissionEntry> entries, string filePath, CancellationToken ct)
|
||||||
|
{
|
||||||
|
var csv = BuildCsv(entries);
|
||||||
|
await File.WriteAllTextAsync(filePath, csv, new UTF8Encoding(encoderShouldEmitUTF8Identifier: true), ct);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>RFC 4180 CSV field escaping: wrap in double quotes, double internal quotes.</summary>
|
/// <summary>RFC 4180 CSV field escaping: wrap in double quotes, double internal quotes.</summary>
|
||||||
private static string Csv(string value)
|
private static string Csv(string value)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user