diff --git a/ApiDoctor.Console/Constants.cs b/ApiDoctor.Console/Constants.cs index ba2a2a06..ded10eaa 100644 --- a/ApiDoctor.Console/Constants.cs +++ b/ApiDoctor.Console/Constants.cs @@ -12,7 +12,7 @@ public static class PermissionsConstants " For details about delegated and application permissions, see [Permission types](/graph/permissions-overview#permission-types). To learn more about these permissions, see the [permissions reference](/graph/permissions-reference)."; public const string MultipleTableBoilerPlateText = "The following tables show the least privileged permission or permissions required to call this API on each supported resource type." + " Follow [best practices](/graph/permissions-overview#best-practices-for-using-microsoft-graph-permissions) to request least privileged permissions." + - " For details about delegated and application permissions, see [Permission types](/graph/permissions-overview#permission-types). To learn more about these permissions, see theĀ [permissions reference](/graph/permissions-reference)."; + " For details about delegated and application permissions, see [Permission types](/graph/permissions-overview#permission-types). To learn more about these permissions, see the [permissions reference](/graph/permissions-reference)."; } public static readonly Regex FunctionParameterRegex = new(@"(?<=\=)[^)]+(?=\))", RegexOptions.Compiled, TimeSpan.FromSeconds(5)); public static readonly Regex QueryOptionSegementRegex = new(@"(\$.*)", RegexOptions.Compiled, TimeSpan.FromSeconds(5)); diff --git a/ApiDoctor.Console/Program.cs b/ApiDoctor.Console/Program.cs index 34c7f1c9..dc4d7051 100644 --- a/ApiDoctor.Console/Program.cs +++ b/ApiDoctor.Console/Program.cs @@ -2647,7 +2647,7 @@ private static async Task GeneratePermissionFilesAsync(GeneratePermissionF bool finishedParsing = false, isBootstrapped = false, ignorePermissionTableUpdate = false, foundAllPermissionTables = false, mergePermissions = false, hasBoilerplateText = false; int insertionStartLine = -1, insertionEndLine = -1, httpRequestStartLine = -1, httpRequestEndLine = -1, boilerplateStartLine = -1, - boilerplateEndLine = -1, permissionsHeaderIndex = -1, codeBlockAnnotationEndLine = -1, permissionsBlockLineCount = -1; + boilerplateEndLine = -1, permissionsHeaderIndex = -1, codeBlockAnnotationEndLine = -1, permissionsBlockLineCount = -1, permissionsTableStartLine = -1; string[] requestUrlsForPermissions = null; for (var currentIndex = 0; currentIndex < originalFileContents.Length && !finishedParsing; currentIndex++) { @@ -2662,8 +2662,8 @@ private static async Task GeneratePermissionFilesAsync(GeneratePermissionF } break; case PermissionsInsertionState.FindInsertionStartLine: - if (foundPermissionTablesOrBlocks == 0 && currentLine.Equals(Constants.PermissionsConstants.DefaultBoilerPlateText, StringComparison.OrdinalIgnoreCase) - || currentLine.Equals(Constants.PermissionsConstants.MultipleTableBoilerPlateText, StringComparison.OrdinalIgnoreCase)) + if (foundPermissionTablesOrBlocks == 0 && (currentLine.Equals(Constants.PermissionsConstants.DefaultBoilerPlateText, StringComparison.OrdinalIgnoreCase) + || currentLine.Equals(Constants.PermissionsConstants.MultipleTableBoilerPlateText, StringComparison.OrdinalIgnoreCase))) { hasBoilerplateText = true; boilerplateStartLine = boilerplateEndLine = currentIndex; @@ -2674,6 +2674,7 @@ private static async Task GeneratePermissionFilesAsync(GeneratePermissionF { isBootstrapped = true; foundPermissionTablesOrBlocks++; + permissionsTableStartLine = currentIndex; insertionEndLine = currentIndex; // [!INCLUDE [permissions-table]... is the end of the insertion block if (!options.BootstrappingOnly) @@ -2717,6 +2718,7 @@ private static async Task GeneratePermissionFilesAsync(GeneratePermissionF } else if (currentLine.Contains('|') && currentLine.Contains("Permission type", StringComparison.OrdinalIgnoreCase)) // found the permissions table { + permissionsTableStartLine = currentIndex; foundPermissionTablesOrBlocks++; var annotation = ExtractCodeBlockAnnotationForPermissionsTable( docFile.DisplayName, @@ -2848,8 +2850,8 @@ private static async Task GeneratePermissionFilesAsync(GeneratePermissionF var permissionFileContents = string.Empty; if (!isBootstrapped) { - var existingPermissionsTable = originalFileContents.Skip(insertionStartLine + 2).Take(insertionEndLine - insertionStartLine - 1); - permissionFileContents = $"{includeFileMetadata}{ConvertToThreeColumnPermissionsTable(existingPermissionsTable)}"; + var existingPermissionsTable = originalFileContents.Skip(permissionsTableStartLine).Take(insertionEndLine - permissionsTableStartLine + 1); + permissionFileContents = $"{includeFileMetadata}{ConvertToThreeColumnPermissionsTable(existingPermissionsTable, docFile.DisplayName)}"; } if (!options.BootstrappingOnly) @@ -2950,7 +2952,7 @@ private static async Task GeneratePermissionFilesAsync(GeneratePermissionF : insertionStartLine + permissionsBlockLineCount - 1; originalFileContents = newFileContents; insertionStartLine = insertionEndLine = httpRequestStartLine = httpRequestEndLine = - codeBlockAnnotationEndLine = permissionsBlockLineCount = -1; + codeBlockAnnotationEndLine = permissionsBlockLineCount = permissionsTableStartLine = -1; mergePermissions = false; requestUrlsForPermissions = null; foundHttpRequestBlocks = 0; @@ -3039,32 +3041,41 @@ private static CodeBlockAnnotation ExtractCodeBlockAnnotationForPermissionsTable return null; } - private static string ConvertToThreeColumnPermissionsTable(IEnumerable tableRows) + private static string ConvertToThreeColumnPermissionsTable(IEnumerable tableRows, string fileName) { var tableString = new StringBuilder("|Permission type|Least privileged permissions|Higher privileged permissions|"); tableString.Append("\r\n|:---|:---|:---|"); foreach (string row in tableRows) { - string[] cells = Regex.Split(row.Trim(), @"\s*\|\s*").Where(static x => !string.IsNullOrWhiteSpace(x)).ToArray(); - - // We already have the 3 column permissions table, abort - if (cells.Length == 3) + try { - return string.Join("\r\n", tableRows); - } + string[] cells = PipeDelimiterRegex.Split(row.Trim()).Where(static x => !string.IsNullOrWhiteSpace(x)).ToArray(); + + // We already have the 3 column permissions table, abort + if (cells.Length == 3) + { + return string.Join("\r\n", tableRows); + } - var allPermissions = cells[1].Trim().Split(',', StringSplitOptions.TrimEntries) - .Where(x => !string.IsNullOrWhiteSpace(x) && !PermissionKeywordsToIgnore.Contains(x)) - .ToList(); + var allPermissions = cells[1].Trim().Split(',', StringSplitOptions.TrimEntries) + .Where(x => !string.IsNullOrWhiteSpace(x) && !PermissionKeywordsToIgnore.Contains(x)) + .ToList(); + + var permissionType = cells[0]; + var leastPrivilegePermission = allPermissions.Any() ? allPermissions.First().Trim() : "Not supported."; + var higherPrivilegePermissions = !allPermissions.Any() + ? "Not supported." + : allPermissions.Count() == 1 + ? "Not available." + : string.Join(", ", allPermissions.Skip(1).Select(x => x.Trim()).ToList()); + tableString.Append($"\r\n|{permissionType}|{leastPrivilegePermission}|{higherPrivilegePermissions}|"); + } + catch (Exception ex) + { + Console.WriteLine($"Could not convert permissions table in {fileName} to three columns: {ex.Message}"); + return string.Join(Environment.NewLine, tableRows); + } - var permissionType = cells[0]; - var leastPrivilegePermission = allPermissions.Any() ? allPermissions.First().Trim() : "Not supported."; - var higherPrivilegePermissions = !allPermissions.Any() - ? "Not supported." - : allPermissions.Count() == 1 - ? "Not available." - : string.Join(", ", allPermissions.Skip(1).Select(x => x.Trim()).ToList()); - tableString.Append($"\r\n|{permissionType}|{leastPrivilegePermission}|{higherPrivilegePermissions}|"); } return tableString.ToString(); } @@ -3194,6 +3205,8 @@ private enum PermissionsInsertionState FindNextPermissionBlock } + private static readonly Regex PipeDelimiterRegex = new Regex(@"\s*\|\s*", RegexOptions.Compiled); + #endregion ///