public void DuplicatesGetIgnored() { var store = Substitute.For <IJiraConfigurationStore>(); var jiraClient = Substitute.For <IJiraRestClient>(); var jiraClientLazy = new Lazy <IJiraRestClient>(() => jiraClient); store.GetBaseUrl().Returns("https://github.com"); store.GetIsEnabled().Returns(true); store.GetJiraUsername().Returns("user"); store.GetJiraPassword().Returns("password".ToSensitiveString()); var jiraIssue = new JiraIssue { Key = "JRE-1234", Fields = new JiraIssueFields { Comments = new JiraIssueComments() } }; jiraClient.GetIssues(Arg.Any <string[]>()).Returns(ResultFromExtension <JiraIssue[]> .Success(new[] { jiraIssue })); var mapper = new WorkItemLinkMapper(store, new CommentParser(), jiraClientLazy, Substitute.For <ISystemLog>()); var workItems = mapper.Map( new OctopusBuildInformation { Commits = new[]
public IResultFromExtension <WorkItemLink[]> Map(OctopusBuildInformation buildInformation) { if (!IsEnabled) { return(ResultFromExtension <WorkItemLink[]> .ExtensionDisabled()); } if (string.IsNullOrEmpty(store.GetJiraUsername()) || string.IsNullOrEmpty(store.GetJiraPassword()?.Value)) { return(FailureWithLog("Username/password not configured")); } var baseUrl = store.GetBaseUrl(); if (string.IsNullOrWhiteSpace(baseUrl)) { return(FailureWithLog("No BaseUrl configured")); } var releaseNotePrefix = store.GetReleaseNotePrefix(); var workItemIds = commentParser.ParseWorkItemIds(buildInformation).Distinct().ToArray(); if (workItemIds.Length == 0) { return(ResultFromExtension <WorkItemLink[]> .Success(Array.Empty <WorkItemLink>())); } return(TryConvertWorkItemLinks(workItemIds, releaseNotePrefix, baseUrl)); }
public string GetWorkItemDescription(string linkData, string releaseNotePrefix, string releaseNote) { var store = Substitute.For <IJiraConfigurationStore>(); var jiraClient = Substitute.For <IJiraRestClient>(); var jiraClientLazy = new Lazy <IJiraRestClient>(() => jiraClient); var jiraIssue = new JiraIssue { Key = linkData, Fields = new JiraIssueFields { Summary = "Test title", Comments = new JiraIssueComments { Total = 1, Comments = new[] { new JiraIssueComment { Body = releaseNote } } } } }; jiraClient.GetIssues(Arg.Any <string[]>()).Returns(ResultFromExtension <JiraIssue[]> .Success(new[] { jiraIssue })); return(new WorkItemLinkMapper(store, new CommentParser(), jiraClientLazy, Substitute.For <ISystemLog>()).GetReleaseNote(jiraIssue, releaseNotePrefix)); }
public async Task <IResultFromExtension <JiraIssue[]> > GetIssues(string[] workItemIds) { var workItemQuery = $"id in ({string.Join(", ", workItemIds.Select(x => x.ToUpper()))})"; // WARNING: while the Jira API documentation says that validateQuery values of true/false are deprecated, // that is only valid for Jira Cloud. Jira Server only supports true/false var content = JsonConvert.SerializeObject( new { jql = workItemQuery, fields = new[] { "summary", "comment" }, maxResults = 10000, validateQuery = "false" }); string errorMessage; try { using var response = await httpClient.PostAsync($"{baseUrl}/{baseApiUri}/search", new StringContent (content, Encoding.UTF8, "application/json")); if (response.IsSuccessStatusCode) { var result = await GetResult <JiraSearchResult>(response); if (result == null) { systemLog.Info("Jira Work Item data not found in response body"); return(ResultFromExtension <JiraIssue[]> .Failed("Jira Work Item data not found in response body")); } systemLog.Info($"Retrieved Jira Work Item data for work item ids {string.Join(", ", result.Issues.Select(wi => wi.Key))}"); return(ResultFromExtension <JiraIssue[]> .Success(result.Issues)); } if (response.StatusCode == HttpStatusCode.Unauthorized || response.StatusCode == HttpStatusCode.Forbidden) { systemLog.Info("Authentication failure, check the Jira access token is valid and has permissions to read work items"); return(ResultFromExtension <JiraIssue[]> .Failed("Authentication failure, check the Jira access token is valid and has permissions to read work items")); } var errorResult = await GetResult <JiraErrorResult>(response); errorMessage = $"Failed to retrieve Jira issues from {baseUrl}. Response Code: {response.StatusCode}{(errorResult?.ErrorMessages.Any() == true ? $" (Errors: {string.Join(", ", errorResult.ErrorMessages)})" : "")}"; } catch (HttpRequestException e) { errorMessage = $"Failed to retrieve Jira issues '{string.Join(", ", workItemIds)}' from {baseUrl}. (Reason: {e.Message})"; } catch (TaskCanceledException e) { errorMessage = $"Failed to retrieve Jira issues '{string.Join(", ", workItemIds)}' from {baseUrl}. (Reason: {e.Message})"; } systemLog.Warn(errorMessage); return(ResultFromExtension <JiraIssue[]> .Failed(errorMessage)); }
public IResultFromExtension DoSomethingWithNoObjectToReturn(Behaviour behaviour) { if (behaviour == Behaviour.Failure) { return(ResultFromExtension.Failed("Some failure reason")); } if (behaviour == Behaviour.Disabled) { return(ResultFromExtension.ExtensionDisabled()); } return(ResultFromExtension.Success()); }
public IResultFromExtension <TestObjectBeingReturned> DoSomething(Behaviour behaviour) { if (behaviour == Behaviour.Failure) { return(ResultFromExtension <TestObjectBeingReturned> .Failed("Some failure reason")); } if (behaviour == Behaviour.Disabled) { return(ResultFromExtension <TestObjectBeingReturned> .ExtensionDisabled()); } return(ResultFromExtension <TestObjectBeingReturned> .Success(new TestObjectBeingReturned("Some Name"))); }
public IResultFromExtension <WorkItemLink[]> Map(OctopusBuildInformation buildInformation) { // For ADO, we should ignore anything that wasn't built by ADO because we get work items from the build if (!IsEnabled) { return(ResultFromExtension <WorkItemLink[]> .ExtensionDisabled()); } if (buildInformation?.BuildEnvironment != "Azure DevOps" || string.IsNullOrWhiteSpace(buildInformation?.BuildUrl)) { return(ResultFromExtension <WorkItemLink[]> .Success(Array.Empty <WorkItemLink>())); } return(client.GetBuildWorkItemLinks(AdoBuildUrls.ParseBrowserUrl(buildInformation.BuildUrl))); }
IResultFromExtension <WorkItemLink[]> TryConvertWorkItemLinks(string[] workItemIds, string?releaseNotePrefix, string baseUrl) { systemLog.InfoFormat("Getting work items {0} from Jira", string.Join(", ", workItemIds)); var response = jira.Value.GetIssues(workItemIds.ToArray()).GetAwaiter().GetResult(); if (response is IFailureResult failureResult) { return(FailureWithLog(failureResult.Errors)); } var issues = ((ISuccessResult <JiraIssue[]>)response).Value; if (issues.Length == 0) { systemLog.InfoFormat("No work items returned from Jira"); return(ResultFromExtension <WorkItemLink[]> .Success(Array.Empty <WorkItemLink>())); } var issueMap = issues.ToDictionary(x => x.Key, StringComparer.OrdinalIgnoreCase); var workItemsNotFound = workItemIds.Where(x => !issueMap.ContainsKey(x)).ToArray(); if (workItemsNotFound.Length > 0) { systemLog.Warn($"Parsed work item ids {string.Join(", ", workItemsNotFound)} from commit messages but could not locate them in Jira"); } return(ResultFromExtension <WorkItemLink[]> .Success( workItemIds .Where(workItemId => issueMap.ContainsKey(workItemId)) .Select( workItemId => { var issue = issueMap[workItemId]; return new WorkItemLink { Id = issue.Key, Description = GetReleaseNote(issueMap[workItemId], releaseNotePrefix), LinkUrl = baseUrl + "/browse/" + workItemId, Source = JiraConfigurationStore.CommentParser }; }) .Where(i => i != null) // ReSharper disable once RedundantEnumerableCastCall .Cast <WorkItemLink>() // cast back from `WorkItemLink?` type to keep the compiler happy .ToArray())); }
public IResultFromExtension <WorkItemLink[]> Map(OctopusBuildInformation buildInformation) { if (!IsEnabled) { return(ResultFromExtension <WorkItemLink[]> .ExtensionDisabled()); } var baseUrl = store.GetBaseUrl(); if (string.IsNullOrWhiteSpace(baseUrl)) { return(ResultFromExtension <WorkItemLink[]> .Failed("Base Url is not configured")); } if (buildInformation.VcsRoot == null) { return(ResultFromExtension <WorkItemLink[]> .Failed("No VCS root configured")); } const string pathComponentIndicatingAzureDevOpsVcs = @"/_git/"; if (buildInformation.VcsRoot.Contains(pathComponentIndicatingAzureDevOpsVcs)) { systemLog.WarnFormat("The VCS Root '{0}' indicates this build information is Azure DevOps related so GitHub comment references will be ignored", buildInformation.VcsRoot); return(ResultFromExtension <WorkItemLink[]> .Success(new WorkItemLink[0])); } var releaseNotePrefix = store.GetReleaseNotePrefix(); var workItemReferences = commentParser.ParseWorkItemReferences(buildInformation); return(ResultFromExtension <WorkItemLink[]> .Success(workItemReferences.Select(wir => new WorkItemLink { Id = wir.IssueNumber, Description = GetReleaseNote(buildInformation.VcsRoot, wir.IssueNumber, wir.LinkData, releaseNotePrefix), LinkUrl = NormalizeLinkData(baseUrl, buildInformation.VcsRoot, wir.LinkData), Source = GitHubConfigurationStore.CommentParser }) .Distinct() .ToArray())); }
IResultFromExtension <WorkItemLink[]> FailureWithLog(params string[] errors) { systemLog.Warn(string.Join(Environment.NewLine, errors)); return(ResultFromExtension <WorkItemLink[]> .Failed(errors)); }