in tools/sdk-ai-bots/AzureSdkQaBot/GithubPrActions.cs [248:372]
public async Task<bool> PullRequestBreakingChangeReview([ActionTurnContext] ITurnContext turnContext, [ActionTurnState] AppState turnState, [ActionEntities] Dictionary<string, object> entities)
{
if (turnContext == null)
{
throw new ArgumentNullException(nameof(turnContext));
}
if (turnState == null)
{
throw new ArgumentNullException(nameof(turnState));
}
string prLink = (string)entities["prLink"];
if (string.IsNullOrEmpty(prLink) || !prLink.Contains("https://"))
{
await _questionAnsweringActions.QuestionAnswering(turnContext, turnState);
return false;
}
// redact query to less than 60 characters
string query = turnContext.Activity.Text;
if (query.Length > 60)
{
query = turnContext.Activity.Text.Substring(0, 57) + "...";
}
this._logger.LogInformation($"PrBreakingChangeReview: PR link:{prLink}. Query: {query}");
(string org, string repo, int prNum) = GetPrInfoFromPrLink(prLink);
string reply = "";
string action = "";
// Get pull request info
if (prNum != 0)
{
(bool isPrMerged, reply) = await IsPrMerged(org, repo, prNum);
if (reply == Constants.Message_Error_Null_PR)
{
reply += $" {prLink}";
}
else if (!isPrMerged)
{
IReadOnlyList<Label> labels = await GetPrLabels(org, repo, prNum);
bool isMgmtPlane = false;
if (labels.Any(x => x.Name == Constants.Label_ResourceManager))
{
isMgmtPlane = true;
}
// check breaking change
(bool completeBcReview, reply, action) = CheckBreakingChangeReview(labels);
if (completeBcReview || reply == "")
{
// sdk breaking change review
if (!labels.Any(x => x.Name == Constants.Label_SDKBreakingChange_Go || x.Name == Constants.Label_SDKBreakingChange_Python || x.Name == Constants.Label_SDKBreakingChange_PythonTrack2 || x.Name == Constants.Label_SDKBreakingChange_JavaScript))
{
reply = Constants.Message_SDKBreakingChangeReview_NotNeeded;
}
else if ((labels.Any(x => x.Name == Constants.Label_SDKBreakingChange_Go) && !labels.Any(x => x.Name == Constants.Label_SDKBreakingChange_Go_Approval))
|| (labels.Any(x => x.Name == Constants.Label_SDKBreakingChange_Python || x.Name == Constants.Label_SDKBreakingChange_PythonTrack2) && !labels.Any(x => x.Name == Constants.Label_SDKBreakingChange_Python_Approval))
|| (labels.Any(x => x.Name == Constants.Label_SDKBreakingChange_JavaScript) && !labels.Any(x => x.Name == Constants.Label_SDKBreakingChange_JavaScript_Approval)))
{
bool completeArmReview = false;
if (isMgmtPlane)
{
(completeArmReview, reply, action) = CheckArmReview(labels);
}
if (isMgmtPlane && !completeArmReview && reply != "")
{
// arm review isn't finished
reply = Constants.Message_ArmReview_NotFinished_BeforeSdkBreakingChangeReview;
action = Constants.Action_ArmReview_NotFinished_BeforeSdkBreakingChangeReview;
}
// arm review completes or not needed
else if (labels.Any(x => x.Name == Constants.Label_SDKBreakingChange_Go) && !labels.Any(x => x.Name == Constants.Label_SDKBreakingChange_Go_Approval))
{
reply = Constants.Message_SDKBreakingChangeGo_NotFinished;
action = Constants.Action_SDKBreakingChangeGo_NotFinished;
}
else if (labels.Any(x => x.Name == Constants.Label_SDKBreakingChange_Python) && !labels.Any(x => x.Name == Constants.Label_SDKBreakingChange_Python_Approval))
{
reply = Constants.Message_SDKBreakingChangePython_NotFinished;
action = Constants.Action_SDKBreakingChangePython_NotFinished;
}
else if (labels.Any(x => x.Name == Constants.Label_SDKBreakingChange_JavaScript) && !labels.Any(x => x.Name == Constants.Label_SDKBreakingChange_JavaScript_Approval))
{
reply = Constants.Message_SDKBreakingChangeJavaScript_NotFinished;
action = Constants.Action_SDKBreakingChangeJavaScript_NotFinished;
}
}
else
{
reply = Constants.Message_BreakingChangeReview_Finished;
action = Constants.Action_BreakingChangeReview_Finished;
}
}
}
// add support message for tool failures
reply = AddSupportMessageForTools(turnContext, reply);
}
// call QA function
(string additionalAnswer, List<string>? relevancies) = await _questionAnsweringActions.QuestionAnsweringHandler(turnContext, turnState);
// get the relevance links based on user input or PR labels to differenciate the mgmt plane and data plane
IList<string> references = await GetRelevanceLinks(turnContext, org, repo, prNum);
Attachment card;
if (additionalAnswer.StartsWith("Sorry, I do not know"))
{
additionalAnswer = "";
relevancies = null;
}
card = await CardBuilder.NewPRAndQAAttachment(query, reply, action, additionalAnswer, references, relevancies, CancellationToken.None);
try
{
await turnContext.SendActivityAsync(MessageFactory.Attachment(card), CancellationToken.None);
}
catch (Exception ex)
{
await turnContext.TraceActivityAsync("SendActivityError", ex, "The bot received an error when send message to Teams.");
Console.WriteLine("Error:" + ex);
this._logger.LogError($"PrBreakingChangeReview: PR link:{prLink}. Query:{query}. Error: {ex}");
}
// End the current chain
return false;
}