diff --git a/CodeSnippetsReflection.OpenAPI.Test/PowerShellGeneratorTests.cs b/CodeSnippetsReflection.OpenAPI.Test/PowerShellGeneratorTests.cs index 676e34299..d8effaa35 100644 --- a/CodeSnippetsReflection.OpenAPI.Test/PowerShellGeneratorTests.cs +++ b/CodeSnippetsReflection.OpenAPI.Test/PowerShellGeneratorTests.cs @@ -358,5 +358,191 @@ public async Task GeneratesSnippetForRequestWithWrongQuotesForStringLiteralsInBo Assert.Contains(expectedParams, result); Assert.Contains("-BodyParameter $params", result); } + + [Fact] + public async Task GeneratesSnippetForDeltaFunctionsWithoutParams() + { + using var requestPayload = new HttpRequestMessage(HttpMethod.Get, $"{ServiceRootUrl}/contacts/delta()?$select=displayName%2CjobTitle%2Cmail"); + var snippetModel = new SnippetModel(requestPayload, ServiceRootUrl, await GetV1SnippetMetadata()); + var result = _generator.GenerateCodeSnippet(snippetModel); + Assert.Contains("Get-MgContactDelta", result); + } + + [Fact] + public async Task GeneratesSnippetForDeltaFunctionsWithParams() + { + using var requestPayload = new HttpRequestMessage(HttpMethod.Get, $"{ServiceRootUrl}/drives/XXXX/items/XXXX/delta(token='token')"); + var snippetModel = new SnippetModel(requestPayload, ServiceRootUrl, await GetV1SnippetMetadata()); + var result = _generator.GenerateCodeSnippet(snippetModel); + Assert.Contains("Get-MgDriveItemDelta", result); + Assert.Contains("-Token", result); + } + + [Fact] + public async Task GeneratesSnippetForHttpSnippetsWithGraphPrefixOnLastPathSegment() + { + using var requestPayload = new HttpRequestMessage(HttpMethod.Get, $"{ServiceRootUrl}/places/graph.room"); + var snippetModel = new SnippetModel(requestPayload, ServiceRootUrl, await GetV1SnippetMetadata()); + var result = _generator.GenerateCodeSnippet(snippetModel); + Assert.Contains("Get-MgPlaceAsRoom", result); + } + + [Fact] + public async Task GeneratesSnippetForHttpSnippetsWithoutGraphPrefixOnLastPathSegment() + { + using var requestPayload = new HttpRequestMessage(HttpMethod.Get, $"{ServiceRootUrl}/places/microsoft.graph.room"); + var snippetModel = new SnippetModel(requestPayload, ServiceRootUrl, await GetV1SnippetMetadata()); + var result = _generator.GenerateCodeSnippet(snippetModel); + Assert.Contains("Get-MgPlaceAsRoom", result); + } + + [Fact] + public async Task GeneratesSnippetForPathsWithIdentityProviderAsRootNode() + { + using var requestPayload = new HttpRequestMessage(HttpMethod.Get, $"{ServiceRootUrl}/identityProviders"); + var snippetModel = new SnippetModel(requestPayload, ServiceRootUrl, await GetV1SnippetMetadata()); + var result = _generator.GenerateCodeSnippet(snippetModel); + Assert.Contains("Get-MgIdentityProvider", result); + } + + [Fact] + public async Task GeneratesSnippetForRequestWithTextContentType() + { + using var requestPayload = new HttpRequestMessage(HttpMethod.Put, $"{ServiceRootUrl}/me/drive/items/XXXX/content") + { + Content = new StringContent( + "Plain text", + Encoding.UTF8, + "text/plain") + }; + var snippetModel = new SnippetModel(requestPayload, ServiceRootUrl, await GetV1SnippetMetadata()); + var result = _generator.GenerateCodeSnippet(snippetModel); + Assert.Contains("Set-MgDriveItemContent", result); + } + + [Fact] + public async Task GeneratesSnippetForRequestWithApplicationZipContentType() + { + using var requestPayload = new HttpRequestMessage(HttpMethod.Put, $"{ServiceRootUrl}/me/drive/items/XXXX/content") + { + Content = new StringContent( + "Zip file content", + Encoding.UTF8, + "application/zip") + }; + var snippetModel = new SnippetModel(requestPayload, ServiceRootUrl, await GetV1SnippetMetadata()); + var result = _generator.GenerateCodeSnippet(snippetModel); + Assert.Contains("Set-MgDriveItemContent", result); + } + + [Fact] + public async Task GeneratesSnippetForRequestWithImageContentType() + { + using var requestPayload = new HttpRequestMessage(HttpMethod.Put, $"{ServiceRootUrl}/me/photo/$value") + { + Content = new StringContent( + "Binary data for the image", + Encoding.UTF8, + "image/jpeg") + }; + var snippetModel = new SnippetModel(requestPayload, ServiceRootUrl, await GetV1SnippetMetadata()); + var result = _generator.GenerateCodeSnippet(snippetModel); + Assert.Contains("Set-MgUserPhotoContent", result); + Assert.Contains("-BodyParameter", result); + } + + [Fact] + public async Task GeneratesBetaSnippetForFunctionsWithoutParams() + { + using var requestPayload = new HttpRequestMessage(HttpMethod.Get, $"{ServiceRootBetaUrl}/contacts/delta()?$select=displayName%2CjobTitle%2Cmail"); + var snippetModel = new SnippetModel(requestPayload, ServiceRootBetaUrl, await GetBetaSnippetMetadata()); + var result = _generator.GenerateCodeSnippet(snippetModel); + Assert.Contains("Get-MgBetaContactDelta", result); + } + + [Fact] + public async Task GeneratesBetaSnippetForFunctionsWithSingleParam() + { + using var requestPayload = new HttpRequestMessage(HttpMethod.Get, $"{ServiceRootBetaUrl}/drives/XXXX/items/XXXX/delta(token='token')"); + var snippetModel = new SnippetModel(requestPayload, ServiceRootBetaUrl, await GetBetaSnippetMetadata()); + var result = _generator.GenerateCodeSnippet(snippetModel); + Assert.Contains("Get-MgBetaDriveItemDelta", result); + Assert.Contains("-Token", result); + } + + [Fact] + public async Task GeneratesBetaSnippetForFunctionsWithMultipleParam() + { + using var requestPayload = new HttpRequestMessage(HttpMethod.Get, $"{ServiceRootBetaUrl}/communications/callRecords/getPstnBlockedUsersLog(fromDateTime=XXXXXX,toDateTime=XXXXX)"); + var snippetModel = new SnippetModel(requestPayload, ServiceRootBetaUrl, await GetBetaSnippetMetadata()); + var result = _generator.GenerateCodeSnippet(snippetModel); + Assert.Contains("Get-MgBetaCommunicationCallRecordPstnBlockedUserLog", result); + Assert.Contains("-ToDateTime", result); + } + + [Fact] + public async Task GeneratesBetaSnippetForHttpSnippetsWithGraphPrefixOnLastPathSegment() + { + using var requestPayload = new HttpRequestMessage(HttpMethod.Get, $"{ServiceRootBetaUrl}/places/graph.room"); + var snippetModel = new SnippetModel(requestPayload, ServiceRootBetaUrl, await GetBetaSnippetMetadata()); + var result = _generator.GenerateCodeSnippet(snippetModel); + Assert.Contains("Get-MgBetaPlaceAsRoom", result); + } + + [Fact] + public async Task GeneratesBetaSnippetForPathsWithIdentityProviderAsRootNode() + { + using var requestPayload = new HttpRequestMessage(HttpMethod.Get, $"{ServiceRootBetaUrl}/identityProviders"); + var snippetModel = new SnippetModel(requestPayload, ServiceRootBetaUrl, await GetBetaSnippetMetadata()); + var result = _generator.GenerateCodeSnippet(snippetModel); + Assert.Contains("Get-MgBetaIdentityProvider", result); + } + + [Fact] + public async Task GeneratesBetaSnippetForRequestWithTextContentType() + { + using var requestPayload = new HttpRequestMessage(HttpMethod.Put, $"{ServiceRootBetaUrl}/me/drive/items/XXXX/content") + { + Content = new StringContent( + "Plain text", + Encoding.UTF8, + "text/plain") + }; + var snippetModel = new SnippetModel(requestPayload, ServiceRootBetaUrl, await GetBetaSnippetMetadata()); + var result = _generator.GenerateCodeSnippet(snippetModel); + Assert.Contains("Set-MgBetaDriveItemContent", result); + } + + [Fact] + public async Task GeneratesBetaSnippetForRequestWithApplicationZipContentType() + { + using var requestPayload = new HttpRequestMessage(HttpMethod.Put, $"{ServiceRootBetaUrl}/me/drive/items/XXXX/content") + { + Content = new StringContent( + "Zip file content", + Encoding.UTF8, + "application/zip") + }; + var snippetModel = new SnippetModel(requestPayload, ServiceRootBetaUrl, await GetBetaSnippetMetadata()); + var result = _generator.GenerateCodeSnippet(snippetModel); + Assert.Contains("Set-MgBetaDriveItemContent", result); + } + + [Fact] + public async Task GeneratesBetaSnippetForRequestWithImageContentType() + { + using var requestPayload = new HttpRequestMessage(HttpMethod.Put, $"{ServiceRootBetaUrl}/me/photo/$value") + { + Content = new StringContent( + "Binary data for the image", + Encoding.UTF8, + "image/jpeg") + }; + var snippetModel = new SnippetModel(requestPayload, ServiceRootBetaUrl, await GetBetaSnippetMetadata()); + var result = _generator.GenerateCodeSnippet(snippetModel); + Assert.Contains("Set-MgBetaUserPhotoContent", result); + Assert.Contains("-BodyParameter", result); + } + } } diff --git a/CodeSnippetsReflection.OpenAPI/LanguageGenerators/PowerShellGenerator.cs b/CodeSnippetsReflection.OpenAPI/LanguageGenerators/PowerShellGenerator.cs index fdb421716..d9856f98d 100644 --- a/CodeSnippetsReflection.OpenAPI/LanguageGenerators/PowerShellGenerator.cs +++ b/CodeSnippetsReflection.OpenAPI/LanguageGenerators/PowerShellGenerator.cs @@ -33,13 +33,23 @@ public class PowerShellGenerator : ILanguageGenerator,]*\)", RegexOptions.Compiled, TimeSpan.FromSeconds(5)); + private static readonly Regex functionWithoutParams = new(@"\w*\(\)", RegexOptions.Compiled, TimeSpan.FromSeconds(5)); public string GenerateCodeSnippet(SnippetModel snippetModel) { var indentManager = new IndentManager(); var snippetBuilder = new StringBuilder(); var cleanPath = snippetModel.EndPathNode.Path.Replace("\\", "/"); var isMeSegment = meSegmentRegex.IsMatch(cleanPath); - var (path, additionalKeySegmentParmeter) = SubstituteMeSegment(isMeSegment, cleanPath); + var hasGraphPrefix = cleanPath.Contains("graph", StringComparison.OrdinalIgnoreCase); + var isIdentityProvider = snippetModel.RootPathNode.Path.StartsWith("\\identityProviders", StringComparison.OrdinalIgnoreCase); + var lastPathSegment = snippetModel.EndPathNode.Segment; + var hasMicrosoftPrefix = lastPathSegment.StartsWith("microsoft", StringComparison.OrdinalIgnoreCase); + cleanPath = SubstituteIdentityProviderSegment(cleanPath, isIdentityProvider); + cleanPath = ReplaceFunctionSegments(lastPathSegment, cleanPath); + cleanPath = SubstituteGraphSegment(cleanPath, hasGraphPrefix); + cleanPath = SubstituteMicrosoftSegment(cleanPath, hasMicrosoftPrefix, lastPathSegment); + var (path, additionalKeySegmentParmeter) = SubstituteMeSegment(isMeSegment, cleanPath, lastPathSegment); IList matchedCommands = GetCommandForRequest(path, snippetModel.Method.ToString(), snippetModel.ApiVersion); var targetCommand = matchedCommands.FirstOrDefault(); if (targetCommand != null) @@ -72,9 +82,13 @@ public string GenerateCodeSnippet(SnippetModel snippetModel) if (RequiresMIMEContentOutPut(snippetModel, path)) { //Allows genration of an output file for MIME content of the message - snippetBuilder.Append($" -OutFile $outFileId"); + snippetBuilder.Append(" -OutFile $outFileId"); } } + else + { + throw new NotImplementedException($"{path} and {snippetModel.Method} operation is not supported in the sdk"); + } return snippetBuilder.ToString(); } /// @@ -92,8 +106,6 @@ private static bool RequiresMIMEContentOutPut(SnippetModel snippetModel, string if (lastValue.Equals("$value") && snippetModel.Method == HttpMethod.Get) return true; return false; } - - private static string GetCommandParameters(SnippetModel snippetModel, string payloadVarName) { var payloadSB = new StringBuilder(); @@ -109,19 +121,24 @@ private static string GetCommandParameters(SnippetModel snippetModel, string pay if (!string.IsNullOrEmpty(parameterList)) payloadSB.Append($" {parameterList}"); + var functionParameterList = GetFunctionParameterList(snippetModel); + if (!string.IsNullOrEmpty(functionParameterList)) + payloadSB.Append($" {functionParameterList}"); + var requestHeadersPayload = GetSupportedRequestHeaders(snippetModel); if (!string.IsNullOrEmpty(requestHeadersPayload)) payloadSB.Append(requestHeadersPayload); + return payloadSB.ToString(); } + public static string ReturnCleanParamsPayload(string queryParamsPayload) { if(encodedQueryParamsPayLoad.IsMatch(queryParamsPayload)) return queryParamsPayload.Replace("+", " "); return queryParamsPayload; } - - private static (string, string) SubstituteMeSegment(bool isMeSegment, string path) + private static (string, string) SubstituteMeSegment(bool isMeSegment, string path, string lastPathSegment) { string additionalKeySegmentParmeter = default; if (isMeSegment) @@ -129,9 +146,44 @@ private static (string, string) SubstituteMeSegment(bool isMeSegment, string pat path = meSegmentRegex.Replace(path, "/users/{user-id}"); additionalKeySegmentParmeter = $" -UserId $userId"; } + if (lastPathSegment.Contains("()")) + { + path = path.RemoveFunctionBraces(); + } return (path, additionalKeySegmentParmeter); } + private static string ReplaceFunctionSegments(string lastPathSegment, string path) + { + var segmentItems = lastPathSegment.Split("("); + if (functionWithoutParams.IsMatch(lastPathSegment) || functionWithParams.IsMatch(lastPathSegment)) + path = path.Replace(lastPathSegment, segmentItems[0]); + return path; + } + + private static string SubstituteGraphSegment(string path, bool hasGraphPrefix) + { + if (hasGraphPrefix) + path = path.Replace("graph.", string.Empty); + return path; + } + private static string SubstituteMicrosoftSegment(string path, bool hasMicrosoftSegment, string lastSegmentPath) + { + if (hasMicrosoftSegment) + { + var splittedPath = path.Split('/'); + path = path.Replace(splittedPath[splittedPath.Length - 1], lastSegmentPath); + } + + return path; + } + private static string SubstituteIdentityProviderSegment(string path, bool isIdentityProvider) + { + if (isIdentityProvider) + path = path.Replace("identityProviders", "identity/identityProviders"); + return path; + } + private static string GetSupportedRequestHeaders(SnippetModel snippetModel) { var payloadSB = new StringBuilder(); @@ -147,7 +199,6 @@ private static string GetSupportedRequestHeaders(SnippetModel snippetModel) } return payloadSB.ToString(); } - private static string GetKeySegmentParameters(IEnumerable pathNodes) { if (!pathNodes.Any()) return string.Empty; @@ -160,7 +211,6 @@ private static string GetKeySegmentParameters(IEnumerable pa return $"{x} {y}"; }); } - private static string GetRequestQueryParameters(SnippetModel model) { var payloadSB = new StringBuilder(); @@ -182,7 +232,6 @@ private static string GetRequestQueryParameters(SnippetModel model) } return default; } - private static string GetQueryParameterValue(string normalizedParameterName, string originalValue, Dictionary replacements) { if (normalizedParameterName.Equals("CountVariable")) @@ -261,6 +310,15 @@ private static (string, string) GetRequestPayloadAndVariableName(SnippetModel sn payloadSB.AppendLine("}"); } break; + case "image/jpeg": + payloadSB.AppendLine($"{indentManager.GetIndent()}${requestBodyVarName} = Binary data for the image"); + break; + case "application/zip": + payloadSB.AppendLine($"{indentManager.GetIndent()}${requestBodyVarName} = {snippetModel?.RequestBody}"); + break; + case "text/plain": + payloadSB.AppendLine($"{indentManager.GetIndent()}${requestBodyVarName} = {snippetModel?.RequestBody}"); + break; default: throw new InvalidOperationException($"Unsupported content type: {snippetModel.ContentType}"); } @@ -352,5 +410,27 @@ private static string GetActionParametersList(params string[] parameters) return string.Join(" ", nonEmptyParameters.Aggregate((a, b) => $"{a}, {b}")); else return string.Empty; } + + private static string GetFunctionParameterList(SnippetModel snippetModel) + { + var snippetPaths = snippetModel.Path; + var paramBuilder = new StringBuilder(); + if (functionWithParams.IsMatch(snippetPaths)) + { + var paths = snippetPaths.Split("/"); + var function = paths.Last(); + var functionItems = function.Split("("); + var functionParameters = functionItems[1].Split(","); + foreach(var param in functionParameters) + { + var paramKeys = param.Split("=")[0]; + var paramKey = $"-{paramKeys.ToFirstCharacterUpperCase()} ${paramKeys}Id "; + paramBuilder.Append(paramKey); + } + + } + return paramBuilder.ToString(); + } + } } diff --git a/KnownIssuesService.Test/WorkItemsStubData.cs b/KnownIssuesService.Test/WorkItemsStubData.cs index 9aec0e292..59ed3e64b 100644 --- a/KnownIssuesService.Test/WorkItemsStubData.cs +++ b/KnownIssuesService.Test/WorkItemsStubData.cs @@ -33,7 +33,7 @@ public static List GetWorkItems() Fields = new Dictionary() { {"System.State","Active"}, {"System.Title","Issue A"}, - {"Custom.MSGraphM365Workload","Calendar"}, + {"Custom.MicrosoftGraphArea","Calendar"}, {"Custom.Workaround","Test"}, {"Custom.PublicIssue", true} } @@ -42,7 +42,7 @@ public static List GetWorkItems() Fields = new Dictionary() { {"System.State","Active"}, {"System.Title","Issue B"}, - {"Custom.MSGraphM365Workload","Notifications"}, + {"Custom.MicrosoftGraphArea","Notifications"}, {"Custom.Workaround","Test"}, {"Custom.APIPathLink", "/foo/bar"}, {"Custom.Dateissuewasraised", DateTime.Parse("01/06/2022 00:00:00")}, @@ -55,7 +55,7 @@ public static List GetWorkItems() Fields = new Dictionary() { {"System.State","Resolved"}, {"System.Title","Issue K"}, - {"Custom.MSGraphM365Workload","Mail"}, + {"Custom.MicrosoftGraphArea","Mail"}, {"Custom.Workaround","Limit number of requests"}, {"Custom.PublicIssue", true} } @@ -64,7 +64,7 @@ public static List GetWorkItems() Fields = new Dictionary() { {"System.State","New"}, {"System.Title","Issue F"}, - {"Custom.MSGraphM365Workload","Mail"}, + {"Custom.MicrosoftGraphArea","Mail"}, {"Custom.Workaround","Limit number of requests"}, {"Custom.PublicIssue", false} } @@ -74,7 +74,7 @@ public static List GetWorkItems() Fields = new Dictionary() { {"System.State","New"}, {"System.Title","Issue A"}, - {"Custom.MSGraphM365Workload","Calendar"}, + {"Custom.MicrosoftGraphArea","Calendar"}, {"Custom.Workaround","Test"}, {"Custom.PublicIssue", true} } diff --git a/KnownIssuesService/Services/KnownIssuesService.cs b/KnownIssuesService/Services/KnownIssuesService.cs index eb9fe662a..dd566bd78 100644 --- a/KnownIssuesService/Services/KnownIssuesService.cs +++ b/KnownIssuesService/Services/KnownIssuesService.cs @@ -166,19 +166,23 @@ public async Task> QueryBugsAsync(string environment, Wiql work Id = x.Id, State = x.Fields.TryGetValue("System.State", out var state) ? state.ToString(): default, Title = x.Fields.TryGetValue("System.Title", out var title) ? title.ToString() : default, - WorkLoadArea = x.Fields.TryGetValue("Custom.MSGraphM365Workload", out var workLoadArea) ? workLoadArea.ToString() : (x.Fields.TryGetValue("Custom.MicrosoftGraphArea", out workLoadArea) ? workLoadArea.ToString() : default), + WorkLoadArea = x.Fields.TryGetValue("Custom.MicrosoftGraphArea", out var workLoadArea) ? workLoadArea.ToString() : default, Description = x.Fields.TryGetValue("System.Description", out var description) ? description.ToString() : default, WorkAround = x.Fields.TryGetValue("Custom.Workaround", out var workAround) ? workAround.ToString() : "Working on it", Link = x.Fields.TryGetValue("Custom.APIPathLink", out var link) ? link.ToString() : default, CreatedDateTime = x.Fields.TryGetValue("Custom.Dateissuewasraised", out DateTime createdDate) ? createdDate : default, LastUpdatedDateTime = x.Fields.TryGetValue("Custom.Lastupdate", out DateTime changedDate) ? changedDate : default, - SubArea = x.Fields.TryGetValue("Custom.MicrosoftGraphSubarea", out var subArea) ? subArea.ToString() : default, + SubArea = x.Fields.TryGetValue("Custom.MicrosoftGraphSubarea", out var subArea) ? subArea.ToString() : default, IsPublicIssue = x.Fields.TryGetValue("Custom.PublicIssue", out bool publicIssue) ? publicIssue : default }).ToList(); foreach(var knownIssue in _knownIssuesList.ToList()) { - if(knownIssue.State == "New" || knownIssue.State == "Closed" || !knownIssue.IsPublicIssue) + if (String.Equals(knownIssue.State, "New", StringComparison.OrdinalIgnoreCase) || + String.Equals(knownIssue.State, "Closed", StringComparison.OrdinalIgnoreCase) || + String.Equals(knownIssue.State, "By design", StringComparison.OrdinalIgnoreCase) || + String.Equals(knownIssue.State, "Unconfirmed", StringComparison.OrdinalIgnoreCase) || + !knownIssue.IsPublicIssue) { _knownIssuesList.Remove(knownIssue); } diff --git a/apidoctor b/apidoctor index 2093dca06..927ac29a7 160000 --- a/apidoctor +++ b/apidoctor @@ -1 +1 @@ -Subproject commit 2093dca066795b91e39d4347dfca5654eb9d1132 +Subproject commit 927ac29a7019df2d71edb1fdee7f65716bf908e8