protected void UpdateStateOfExternalItem(Card card, List<string> states, BoardMapping mapping, bool runOnlyOnce) { if (string.IsNullOrEmpty(card.ExternalSystemName) || !card.ExternalSystemName.Equals(ServiceName, StringComparison.OrdinalIgnoreCase)) return; if (string.IsNullOrEmpty(card.ExternalCardID)) { Log.Debug("Ignoring card [{0}] with missing external id value.", card.Id); return; } if (states == null || states.Count == 0) return; int tries = 0; bool success = false; while (tries < 10 && !success && (!runOnlyOnce || tries == 0)) { if (tries > 0) { Log.Warn(string.Format("Attempting to update external work item [{0}] attempt number [{1}]", card.ExternalCardID, tries)); // wait 5 seconds before trying again Thread.Sleep(new TimeSpan(0, 0, 5)); } //https://yoursite.atlassian.net/rest/api/latest/issue/{issueIdOrKey} var request = CreateRequest(string.Format("rest/api/latest/issue/{0}", card.ExternalCardID), Method.GET); var jiraResp = ExecuteRequest(request); if (jiraResp.StatusCode != HttpStatusCode.OK) { var serializer = new JsonSerializer<ErrorMessage>(); var errorMessage = serializer.DeserializeFromString(jiraResp.Content); Log.Error( string.Format( "Unable to get issues from Jira, Error: {0}. Check your board/repo mapping configuration.", errorMessage.Message)); } else { var issueToUpdate = new JsonSerializer<Issue>().DeserializeFromString(jiraResp.Content); // Check for a workflow mapping to the closed state if (states != null && states.Count > 0 && states[0].Contains(">")) { var workflowStates = states[0].Split('>'); // check to see if the workitem is already in one of the workflow states var alreadyInState = workflowStates.FirstOrDefault( x => x.Trim().ToLowerInvariant() == issueToUpdate.Fields.Status.Name.ToLowerInvariant()); if (!string.IsNullOrEmpty(alreadyInState)) { // change workflowStates to only use the states after the currently set state var currentIndex = Array.IndexOf(workflowStates, alreadyInState); if (currentIndex < workflowStates.Length - 1) { var updatedWorkflowStates = new List<string>(); for (int i = currentIndex + 1; i < workflowStates.Length; i++) { updatedWorkflowStates.Add(workflowStates[i]); } workflowStates = updatedWorkflowStates.ToArray(); } } if (workflowStates.Length > 0) { foreach (string workflowState in workflowStates) { UpdateStateOfExternalItem(card, new List<string> {workflowState.Trim()}, mapping, runOnlyOnce); } return; } } foreach (var state in states) { if (issueToUpdate.Fields.Status.Name.ToLowerInvariant() == state.ToLowerInvariant()) { Log.Debug(string.Format("Issue [{0}] is already in state [{1}]", issueToUpdate.Key, state)); return; } } try { // first get a list of available transitions var transitionsRequest = CreateRequest( string.Format("rest/api/2/issue/{0}/transitions?expand=transitions.fields", card.ExternalCardID), Method.GET); var transitionsResponse = ExecuteRequest(transitionsRequest); if (transitionsResponse.StatusCode != HttpStatusCode.OK) { var serializer = new JsonSerializer<ErrorMessage>(); var errorMessage = serializer.DeserializeFromString(jiraResp.Content); Log.Error(string.Format("Unable to get available transitions from Jira, Error: {0}.", errorMessage.Message)); } else { var availableTransitions = new JsonSerializer<TransitionsResponse>().DeserializeFromString( transitionsResponse.Content); if (availableTransitions != null && availableTransitions.Transitions != null && availableTransitions.Transitions.Any()) { // now find match from available transitions to states var valid = false; Transition validTransition = null; foreach (var st in states) { validTransition = availableTransitions.Transitions.FirstOrDefault( x => x.Name.ToLowerInvariant() == st.ToLowerInvariant() || x.To.Name.ToLowerInvariant() == st.ToLowerInvariant()); if (validTransition != null) { // if you find one then set it valid = true; break; } } if (!valid) { // if not then write an error message Log.Error( string.Format( "Unable to update Issue [{0}] to [{1}] because the status transition is invalid. Try adding additional states to the config.", card.ExternalCardID, states.Join(","))); } else { // go ahead and try to update the state of the issue in JIRA //https://yoursite.atlassian.net/rest/api/latest/issue/{issueIdOrKey}/transitions?expand=transitions.fields var updateRequest = CreateRequest( string.Format( "rest/api/latest/issue/{0}/transitions?expand=transitions.fields", card.ExternalCardID), Method.POST); updateRequest.AddParameter("application/json", "{ \"transition\": { \"id\": \"" + validTransition.Id + "\"}}", ParameterType.RequestBody); var resp = ExecuteRequest(updateRequest); if (resp.StatusCode != HttpStatusCode.OK && resp.StatusCode != HttpStatusCode.NoContent) { var serializer = new JsonSerializer<ErrorMessage>(); var errorMessage = serializer.DeserializeFromString(resp.Content); Log.Error( string.Format( "Unable to update Issue [{0}] to [{1}], Description: {2}, Message: {3}", card.ExternalCardID, validTransition.To.Name, resp.StatusDescription, errorMessage.Message)); } else { success = true; Log.Debug(String.Format("Updated state for Issue [{0}] to [{1}]", card.ExternalCardID, validTransition.To.Name)); } } } else { Log.Error( string.Format( "Unable to update Issue [{0}] to [{1}] because no transitions were available from its current status [{2}]. The user account you are using to connect may not have proper privileges.", card.ExternalCardID, states.Join(","), issueToUpdate.Fields.Status.Name)); } } } catch (Exception ex) { Log.Error(string.Format("Unable to update Issue [{0}] to [{1}], Exception: {2}", card.ExternalCardID, states.Join(","), ex.Message)); } } tries++; } }
protected override void CardUpdated(Card updatedCard, List<string> updatedItems, BoardMapping boardMapping) { if (!updatedCard.ExternalSystemName.Equals(ServiceName, StringComparison.OrdinalIgnoreCase)) return; if (string.IsNullOrEmpty(updatedCard.ExternalCardID)) return; if (string.IsNullOrEmpty(updatedCard.ExternalCardID)) { Log.Debug("Ignoring card [{0}] with missing external id value.", updatedCard.ExternalCardID); return; } //https://yoursite.atlassian.net/rest/api/latest/issue/{issueIdOrKey} var request = CreateRequest(string.Format("rest/api/latest/issue/{0}", updatedCard.ExternalCardID), Method.GET); var jiraResp = ExecuteRequest(request); if (jiraResp.StatusCode != HttpStatusCode.OK) { var serializer = new JsonSerializer<ErrorMessage>(); var errorMessage = serializer.DeserializeFromString(jiraResp.Content); Log.Error( string.Format( "Unable to get issues from Jira, Error: {0}. Check your board/repo mapping configuration.", errorMessage.Message)); } else { var issueToUpdate = new JsonSerializer<Issue>().DeserializeFromString(jiraResp.Content); if (issueToUpdate != null && issueToUpdate.Key == updatedCard.ExternalCardID) { bool isDirty = false; bool updateEpicName = false; var updateJson = "{ \"fields\": { "; if (updatedItems.Contains("Title") && issueToUpdate.Fields.Summary != updatedCard.Title) { issueToUpdate.Fields.Summary = updatedCard.Title; isDirty = true; updateEpicName = true; } updateJson += "\"summary\": \"" + issueToUpdate.Fields.Summary.Replace("\"", "\\\"") + "\""; if (updateEpicName) { if (issueToUpdate.Fields.IssueType.Name.ToLowerInvariant() == "epic") { if (CustomFields.Any()) { var epicNameField = CustomFields.FirstOrDefault(x => x.Name == "Epic Name"); if (epicNameField != null) { updateJson += ", \"" + epicNameField.Id + "\": \"" + updatedCard.Title.Replace("\"", "\\\"") + "\""; } } } } if (updatedItems.Contains("Description") && issueToUpdate.Fields.Description.SanitizeCardDescription().JiraPlainTextToLeanKitHtml() != updatedCard.Description) { var updatedDescription = updatedCard.Description.LeanKitHtmlToJiraPlainText(); updateJson += ", \"description\": \"" + updatedDescription + "\""; isDirty = true; } if (updatedItems.Contains("Priority")) { updateJson += ", \"priority\": { \"name\": \"" + GetPriority(updatedCard.Priority) + "\"}"; isDirty = true; } if (updatedItems.Contains("DueDate") && CurrentUser != null) { try { var dateFormat = CurrentUser.DateFormat ?? "MM/dd/yyyy"; var parsed = DateTime.ParseExact(updatedCard.DueDate, dateFormat, CultureInfo.InvariantCulture); updateJson += ", \"duedate\": \"" + parsed.ToString("o") + "\""; } catch (Exception ex) { Log.Warn(ex, "Could not parse due date: {0}", updatedCard.DueDate); } } if (updatedItems.Contains("Tags")) { var newLabels = updatedCard.Tags.Split(','); string updateLabels = ""; int ctr = 0; foreach (string newLabel in newLabels) { if (ctr > 0) updateLabels += ", "; updateLabels += "\"" + newLabel.Trim() + "\""; ctr++; } updateJson += ", \"labels\": [" + updateLabels + "]"; isDirty = true; } string comment = ""; if (updatedItems.Contains("Size")) { comment += "LeanKit card Size changed to " + updatedCard.Size + ". "; } if (updatedItems.Contains("Blocked")) { if (updatedCard.IsBlocked) comment += "LeanKit card is blocked: " + updatedCard.BlockReason + ". "; else comment += "LeanKit card is no longer blocked: " + updatedCard.BlockReason + ". "; } updateJson += "}}"; if (isDirty) { try { //https://yoursite.atlassian.net/rest/api/latest/issue/{issueIdOrKey} var updateRequest = CreateRequest(string.Format("rest/api/latest/issue/{0}", updatedCard.ExternalCardID), Method.PUT); updateRequest.AddParameter("application/json", updateJson, ParameterType.RequestBody); var resp = ExecuteRequest(updateRequest); if (resp.StatusCode != HttpStatusCode.OK && resp.StatusCode != HttpStatusCode.NoContent) { var serializer = new JsonSerializer<ErrorMessage>(); var errorMessage = serializer.DeserializeFromString(resp.Content); Log.Error(string.Format("Unable to update Issue [{0}], Description: {1}, Message: {2}", updatedCard.ExternalCardID, resp.StatusDescription, errorMessage.Message)); } else { Log.Debug(String.Format("Updated Issue [{0}]", updatedCard.ExternalCardID)); } } catch (Exception ex) { Log.Error(string.Format("Unable to update Issue [{0}], Exception: {1}", updatedCard.ExternalCardID, ex.Message)); } } if (!string.IsNullOrEmpty(comment)) { try { //https://yoursite.atlassian.net/rest/api/latest/issue/{issueIdOrKey} var updateRequest = CreateRequest( string.Format("rest/api/latest/issue/{0}/comment", updatedCard.ExternalCardID), Method.POST); updateRequest.AddParameter( "application/json", "{ \"body\": \"" + comment + "\"}", ParameterType.RequestBody); var resp = ExecuteRequest(updateRequest); if (resp.StatusCode != HttpStatusCode.OK && resp.StatusCode != HttpStatusCode.NoContent && resp.StatusCode != HttpStatusCode.Created) { var serializer = new JsonSerializer<ErrorMessage>(); var errorMessage = serializer.DeserializeFromString(resp.Content); Log.Error( string.Format( "Unable to create comment for updated Issue [{0}], Description: {1}, Message: {2}", updatedCard.ExternalCardID, resp.StatusDescription, errorMessage.Message)); } else { Log.Debug(String.Format("Created comment for updated Issue [{0}]", updatedCard.ExternalCardID)); } } catch (Exception ex) { Log.Error(string.Format("Unable to create comment for updated Issue [{0}], Exception: {1}", updatedCard.ExternalCardID, ex.Message)); } } } } }
protected override void Synchronize(BoardMapping project) { Log.Debug("Polling Jira for Issues"); var queryAsOfDate = QueryDate.AddMilliseconds(Configuration.PollingFrequency*-1.5); string jqlQuery; var formattedQueryDate = queryAsOfDate.ToString(QueryDateFormat, CultureInfo.InvariantCulture); if (!string.IsNullOrEmpty(project.Query)) { jqlQuery = string.Format(project.Query, formattedQueryDate); } else { var queryFilter = string.Format(" and ({0})", string.Join(" or ", project.QueryStates.Select(x => "status = '" + x.Trim() + "'").ToList())); if (!string.IsNullOrEmpty(project.ExcludedTypeQuery)) { queryFilter += project.ExcludedTypeQuery; } jqlQuery = string.Format("project=\"{0}\" {1} and updated > \"{2}\" order by created asc", project.Identity.Target, queryFilter, formattedQueryDate); } //https://yoursite.atlassian.net/rest/api/latest/search?jql=project=%22More+Tests%22+and+status=%22open%22+and+created+%3E+%222008/12/31+12:00%22+order+by+created+asc&fields=id,status,priority,summary,description var request = CreateRequest("rest/api/latest/search", Method.GET); request.AddParameter("jql", jqlQuery); request.AddParameter("fields", "id,status,priority,summary,description,issuetype,type,assignee,duedate,labels"); request.AddParameter("maxResults", "9999"); var jiraResp = ExecuteRequest(request); if (jiraResp.StatusCode != HttpStatusCode.OK) { var serializer = new JsonSerializer<ErrorMessage>(); var errorMessage = serializer.DeserializeFromString(jiraResp.Content); Log.Error( string.Format( "Unable to get issues from Jira, Error: {0}. Check your board/project mapping configuration.", errorMessage.Message)); return; } var resp = new JsonSerializer<IssuesResponse>().DeserializeFromString(jiraResp.Content); Log.Info("\nQueried [{0}] at {1} for changes after {2}", project.Identity.Target, QueryDate, queryAsOfDate.ToString("o")); if (resp != null && resp.Issues != null && resp.Issues.Any()) { var issues = resp.Issues; foreach (var issue in issues) { Log.Info("Issue [{0}]: {1}, {2}, {3}", issue.Key, issue.Fields.Summary, issue.Fields.Status.Name, issue.Fields.Priority.Name); // does this workitem have a corresponding card? var card = LeanKit.GetCardByExternalId(project.Identity.LeanKit, issue.Key); if (card == null || !card.ExternalSystemName.Equals(ServiceName, StringComparison.OrdinalIgnoreCase)) { Log.Debug("Create new card for Issue [{0}]", issue.Key); CreateCardFromItem(project, issue); } else { Log.Debug("Previously created a card for Issue [{0}]", issue.Key); if (project.UpdateCards) IssueUpdated(issue, card, project); else Log.Info("Skipped card update because 'UpdateCards' is disabled."); } } Log.Info("{0} item(s) queried.\n", issues.Count); } }
protected override void OnArrange() { base.OnArrange(); MockLeanKitApi.Setup(x => x.GetBoard(It.IsAny<long>())).Returns(_testBoard); var serializer = new JsonSerializer<GitHubPulls.Pull>(); var pull1 = new GitHubPulls.Pull() { Id = 1, Number = 1, State = "Open" }; var restResponse1 = new RestResponse() { Content = serializer.SerializeToString(pull1), StatusCode = HttpStatusCode.OK }; MockRestClient.Setup( x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("pulls/1") && y.Method == Method.GET))) .Returns(restResponse1); MockRestClient.Setup( x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("pulls/1") && y.Method == Method.PATCH))) .Returns(restResponse1); var pull2 = new GitHubPulls.Pull() { Id = 2, Number = 2, State = "Closed" }; var restResponse2 = new RestResponse() { Content = serializer.SerializeToString(pull2), StatusCode = HttpStatusCode.OK }; MockRestClient.Setup( x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("pulls/2") && y.Method == Method.GET))) .Returns(restResponse2); MockRestClient.Setup( x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("pulls/2") && y.Method == Method.PATCH))) .Returns(restResponse2); var errorSerializer = new JsonSerializer<GitHubPulls.ErrorMessage>(); var errorResponse = new RestResponse() { Content = errorSerializer.SerializeToString(new GitHubPulls.ErrorMessage() {Message = "Error"}), StatusCode = HttpStatusCode.NotFound }; MockRestClient.Setup( x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("pulls/3") && y.Method == Method.GET))) .Returns(errorResponse); MockRestClient.Setup( x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("pulls/3") && y.Method == Method.PATCH))) .Returns(errorResponse); }
protected override void OnArrange() { base.OnArrange(); MockLeanKitApi.Setup(x => x.GetBoard(It.IsAny<long>())).Returns(_testBoard); var serializer = new JsonSerializer<Jira.Issue>(); var prioritySerializer = new JsonSerializer<Jira.Priority[]>(); var issue1 = new Jira.Issue() { Id = 1, Key = "one", Fields = new Jira.Fields() { Status = new Jira.Status() {Name = "Open"}, Description = "Issue 1", Summary = "Issue 1" } }; var priorities = new List<Jira.Priority> { new Jira.Priority { Description = "Blocks", Id = "1", Name = "Blocker" }, new Jira.Priority { Description = "Crashes", Id = "2", Name = "Critical" }, new Jira.Priority { Description = "Major", Id = "3", Name = "Major" }, new Jira.Priority { Description = "Minor", Id = "4", Name = "Minor" }, new Jira.Priority { Description = "Trivial", Id = "1", Name = "Trivial" } }.ToArray(); var restResponse1 = new RestResponse() { Content = serializer.SerializeToString(issue1), StatusCode = HttpStatusCode.OK }; var restResponse2 = new RestResponse() { Content = prioritySerializer.SerializeToString(priorities), StatusCode = HttpStatusCode.OK }; MockRestClient.Setup( x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("latest/issue") && y.Method == Method.POST))) .Returns(restResponse1); MockRestClient.Setup( x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("latest/priority") && y.Method == Method.GET))) .Returns(restResponse2); }
protected override void OnArrange() { base.OnArrange(); MockLeanKitApi.Setup(x => x.GetBoard(It.IsAny<long>())).Returns(_testBoard); var serializer = new JsonSerializer<Unfuddle.Ticket>(); var ticket1 = new Unfuddle.Ticket() { Id = 1, Status = "Open" }; var restResponse1 = new RestResponse() { Content = serializer.SerializeToString(ticket1), StatusCode = HttpStatusCode.OK }; MockRestClient.Setup(x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("tickets/1") && y.Method == Method.GET))).Returns(restResponse1); MockRestClient.Setup(x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("tickets/1") && y.Method == Method.PUT))).Returns(restResponse1); var ticket2 = new Unfuddle.Ticket() { Id = 2, Status = "Closed" }; var restResponse2 = new RestResponse() { Content = serializer.SerializeToString(ticket2), StatusCode = HttpStatusCode.OK }; MockRestClient.Setup(x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("tickets/2") && y.Method == Method.GET))).Returns(restResponse2); MockRestClient.Setup(x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("tickets/2") && y.Method == Method.PUT))).Returns(restResponse2); var errorSerializer = new JsonSerializer<Unfuddle.ErrorMessage>(); var errorResponse = new RestResponse() { Content = errorSerializer.SerializeToString(new Unfuddle.ErrorMessage() { Message = "Error" }), StatusCode = HttpStatusCode.NotFound }; MockRestClient.Setup(x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("tickets/3") && y.Method == Method.GET))).Returns(errorResponse); MockRestClient.Setup(x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("tickets/3") && y.Method == Method.PUT))).Returns(errorResponse); }
protected override void OnArrange() { base.OnArrange(); MockLeanKitApi.Setup(x => x.GetBoard(It.IsAny<long>())).Returns(_testBoard); var serializer = new JsonSerializer<Unfuddle.TicketsResponse>(); var ticket1 = new Unfuddle.Ticket() { Id = 1, Status = "Open", Description = "Ticket 1", Summary = "Ticket 1" }; var ticket2 = new Unfuddle.Ticket() { Id = 2, Status = "Open", Description = "Ticket 2", Summary = "Ticket 2" }; var ticket3 = new Unfuddle.Ticket() { Id = 3, Status = "Open", Description = "Ticket 3", Summary = "Ticket 3" }; var group1 = new Unfuddle.Group() { Tickets = new List<Unfuddle.Ticket>() {ticket1} }; var unfuddleResponse1 = new Unfuddle.TicketsResponse() { Count = 1, Groups = new List<Unfuddle.Group>() {group1} }; var restResponse1 = new RestResponse() { Content = serializer.SerializeToString(unfuddleResponse1), StatusCode = HttpStatusCode.OK }; MockRestClient.Setup(x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("projects/1/ticket_reports") && y.Method == Method.GET))).Returns(restResponse1); MockLeanKitApi.Setup(x => x.GetCardByExternalId(1, It.IsAny<string>())).Returns((Card)null); MockLeanKitApi.Setup(x => x.AddCard(1, It.IsAny<Card>(), It.IsAny<string>())).Returns(_testCardAddResult1); MockRestClient.Setup(x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("projects/2/ticket_reports") && y.Method == Method.GET))).Returns(restResponse1); MockLeanKitApi.Setup(x => x.GetCardByExternalId(2, It.IsAny<string>())).Returns((Card)null); MockLeanKitApi.Setup(x => x.AddCard(2, It.IsAny<Card>(), It.IsAny<string>())).Returns(_testCardAddResult1); var group3 = new Unfuddle.Group() { Tickets = new List<Unfuddle.Ticket>() { ticket1, ticket2, ticket3 } }; var unfuddleResponse3 = new Unfuddle.TicketsResponse() { Count = 1, Groups = new List<Unfuddle.Group>() { group3 } }; var restResponse3 = new RestResponse() { Content = serializer.SerializeToString(unfuddleResponse3), StatusCode = HttpStatusCode.OK }; MockRestClient.Setup(x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("projects/3/ticket_reports") && y.Method == Method.GET))).Returns(restResponse3); MockLeanKitApi.Setup(x => x.GetCardByExternalId(3, It.IsAny<string>())).Returns((Card)null); MockLeanKitApi.Setup(x => x.AddCard(3, It.IsAny<Card>(), It.IsAny<string>())).Returns(_testCardAddResult1); MockRestClient.Setup(x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("projects/4/ticket_reports") && y.Method == Method.GET))).Returns(restResponse1); MockLeanKitApi.Setup(x => x.GetCardByExternalId(4, It.IsAny<string>())).Returns(new Card() { Id = 4, ExternalSystemName = "Unfuddle" }); MockLeanKitApi.Setup(x => x.AddCard(4, It.IsAny<Card>(), It.IsAny<string>())).Returns(_testCardAddResult1); MockRestClient.Setup(x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("projects/5/ticket_reports") && y.Method == Method.GET))).Returns(restResponse1); MockLeanKitApi.Setup(x => x.GetCardByExternalId(5, It.IsAny<string>())).Returns(new Card() { Id = 4, ExternalSystemName = "Unfuddlest" }); MockLeanKitApi.Setup(x => x.AddCard(5, It.IsAny<Card>(), It.IsAny<string>())).Returns(_testCardAddResult1); }
protected override void CardUpdated(Card updatedCard, List<string> updatedItems, BoardMapping boardMapping) { if (!updatedCard.ExternalSystemName.Equals(ServiceName, StringComparison.OrdinalIgnoreCase)) return; var target = boardMapping.Identity.Target; if (string.IsNullOrEmpty(updatedCard.ExternalCardID)) { Log.Debug("Ignoring card [{0}] with missing external id value.", updatedCard.ExternalCardID); return; } //https://mysubdomain.unfuddle.com/api/v1/projects/{id}/tickets/{id} var request = new RestRequest(string.Format("/api/v1/projects/{0}/tickets/{1}", target, updatedCard.ExternalCardID), Method.GET); var unfuddleResp = _restClient.Execute(request); if (unfuddleResp.StatusCode != HttpStatusCode.OK) { var serializer = new JsonSerializer<ErrorMessage>(); var errorMessage = serializer.DeserializeFromString(unfuddleResp.Content); Log.Error(string.Format("Unable to get tickets from Unfuddle, Error: {0}. Check your board/repo mapping configuration.", errorMessage.Message)); } else { var ticketToUpdate = new JsonSerializer<Ticket>().DeserializeFromString(unfuddleResp.Content); if (ticketToUpdate != null && ticketToUpdate.Id.ToString() == updatedCard.ExternalCardID) { bool isDirty = false; string summaryXml = ""; string descriptionXml = ""; string priorityXml = ""; string dueDateXml = ""; if (updatedItems.Contains("Title") && ticketToUpdate.Summary != updatedCard.Title) { summaryXml = "<summary>" + updatedCard.Title + "</summary>"; isDirty = true; } if (updatedItems.Contains("Description") && ticketToUpdate.Description.SanitizeCardDescription() != updatedCard.Description) { var updatedDescription = updatedCard.Description; if (!string.IsNullOrEmpty(updatedDescription)) { updatedDescription = updatedDescription.Replace("<p>", "").Replace("</p>", ""); } descriptionXml = "<description>" + updatedDescription + "</description>"; isDirty = true; } if (updatedItems.Contains("Priority")) { priorityXml = "<priority>" + GetPriority(updatedCard.Priority) + "</priority>"; isDirty = true; } if (updatedItems.Contains("DueDate")) { DateTime updatedDate; var isDate = DateTime.TryParse(updatedCard.DueDate, out updatedDate); if (isDate) { dueDateXml = "<due-on>" + updatedDate.ToString("o") + "</due-on>"; isDirty = true; } } string comment = ""; if (updatedItems.Contains("Size")) { comment += "LeanKit card Size changed to " + updatedCard.Size + ". "; } if (updatedItems.Contains("Tags")) { comment += "LeanKit card Tags changed to " + updatedCard.Tags + ". "; } if (updatedItems.Contains("Blocked")) { if (updatedCard.IsBlocked) comment += "LeanKit card is blocked: " + updatedCard.BlockReason + ". "; else comment += "LeanKit card is no longer blocked: " + updatedCard.BlockReason + ". "; } if (isDirty) { try { //https://mysubdomain.unfuddle.com/api/v1/api/v1/projects/{id}/tickets/{id} var updateRequest = new RestRequest(string.Format("/api/v1/projects/{0}/tickets/{1}", target, updatedCard.ExternalCardID), Method.PUT); updateRequest.AddHeader("Accept", "application/json"); updateRequest.AddHeader("Content-type", "application/xml"); updateRequest.AddParameter( "application/xml", string.Format("<ticket>{0}{1}{2}{3}</ticket>", summaryXml, descriptionXml, priorityXml, dueDateXml), ParameterType.RequestBody ); var resp = _restClient.Execute(updateRequest); if (resp.StatusCode != HttpStatusCode.OK) { if (resp.Content != null && !string.IsNullOrEmpty(resp.Content.Trim())) { var serializer = new JsonSerializer<ErrorMessage>(); var errorMessage = serializer.DeserializeFromString(resp.Content); Log.Error(string.Format("Unable to update Ticket [{0}], Description: {1}, Message: {2}", updatedCard.ExternalCardID, resp.StatusDescription, errorMessage.Message)); } else { Log.Error(string.Format("Unable to update Ticket [{0}], Description: {1}, Message: {2}", updatedCard.ExternalCardID, resp.StatusDescription, resp.StatusDescription)); } } else { Log.Debug(String.Format("Updated Ticket [{0}]", updatedCard.ExternalCardID, ticketToUpdate.Status)); } } catch (Exception ex) { Log.Error(string.Format("Unable to update Ticket [{0}], Exception: {1}", updatedCard.ExternalCardID, ex.Message)); } } if (!string.IsNullOrEmpty(comment)) { try { //https://mysubdomain.unfuddle.com/api/v1/api/v1/projects/{id}/tickets/{id}/comments var updateRequest = new RestRequest(string.Format("/api/v1/projects/{0}/tickets/{1}/comments", target, updatedCard.ExternalCardID), Method.POST); updateRequest.AddHeader("Accept", "application/json"); updateRequest.AddHeader("Content-type", "application/xml"); updateRequest.AddParameter( "application/xml", string.Format("<comment><body>{0}</body></comment>", comment), ParameterType.RequestBody ); var resp = _restClient.Execute(updateRequest); if (resp.StatusCode != HttpStatusCode.OK && resp.StatusCode != HttpStatusCode.Created) { if (resp.Content != null && !string.IsNullOrEmpty(resp.Content.Trim())) { var serializer = new JsonSerializer<ErrorMessage>(); var errorMessage = serializer.DeserializeFromString(resp.Content); Log.Error(string.Format("Unable to create comment on updated Ticket [{0}], Description: {1}, Message: {2}", updatedCard.ExternalCardID, resp.StatusDescription, errorMessage.Message)); } else { Log.Error(string.Format("Unable to create comment on updated Ticket [{0}], Description: {1}, Message: {2}", updatedCard.ExternalCardID, resp.StatusDescription, resp.StatusDescription)); } } else { Log.Debug(String.Format("Created comment on updated Ticket [{0}]", updatedCard.ExternalCardID, ticketToUpdate.Status)); } } catch (Exception ex) { Log.Error(string.Format("Unable to create comment on updated Ticket [{0}], Exception: {1}", updatedCard.ExternalCardID, ex.Message)); } } } } }
protected override void OnArrange() { base.OnArrange(); MockLeanKitApi.Setup(x => x.GetBoard(It.IsAny<long>())).Returns(_testBoard); var serializer = new JsonSerializer<List<GitHubIssues.Issue>>(); var issue1 = new GitHubIssues.Issue() { Id = 1, Number = 1, State = "Open", Body = "New Issue 1", Title = "New Issue 1" }; var issue2 = new GitHubIssues.Issue() { Id = 2, Number = 2, State = "Open", Body = "New Issue 2", Title = "New Issue 2" }; var issue3 = new GitHubIssues.Issue() { Id = 3, Number = 3, State = "Open", Body = "New Issue 3", Title = "New Issue 3" }; var restResponse1 = new RestResponse() { Content = serializer.SerializeToString(new List<GitHubIssues.Issue>() { issue1 }), StatusCode = HttpStatusCode.OK }; var noRestResponse = new RestResponse() {StatusCode = HttpStatusCode.OK, Content = ""}; MockRestClient.Setup(x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("/1/issues") && y.Method == Method.GET && y.Parameters[0].Value.ToString() == "Open"))).Returns(restResponse1); MockRestClient.Setup(x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("/1/issues") && y.Method == Method.GET && y.Parameters[0].Value.ToString() != "Open"))).Returns(noRestResponse); MockLeanKitApi.Setup(x => x.GetCardByExternalId(1, It.IsAny<string>())).Returns((Card)null); MockLeanKitApi.Setup(x => x.AddCard(1, It.IsAny<Card>(), It.IsAny<string>())).Returns(_testCardAddResult1); MockRestClient.Setup(x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("/2/issues") && y.Method == Method.GET && y.Parameters[0].Value.ToString() == "Open"))).Returns(restResponse1); MockRestClient.Setup(x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("/2/issues") && y.Method == Method.GET && y.Parameters[0].Value.ToString() != "Open"))).Returns(noRestResponse); MockLeanKitApi.Setup(x => x.GetCardByExternalId(2, It.IsAny<string>())).Returns((Card)null); MockLeanKitApi.Setup(x => x.AddCard(2, It.IsAny<Card>(), It.IsAny<string>())).Returns(_testCardAddResult1); var restResponse3 = new RestResponse() { Content = serializer.SerializeToString(new List<GitHubIssues.Issue>() {issue1, issue2, issue3}), StatusCode = HttpStatusCode.OK }; MockRestClient.Setup(x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("/3/issues") && y.Method == Method.GET && y.Parameters[0].Value.ToString() == "Open"))).Returns(restResponse3); MockRestClient.Setup(x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("/3/issues") && y.Method == Method.GET && y.Parameters[0].Value.ToString() != "Open"))).Returns(noRestResponse); MockLeanKitApi.Setup(x => x.GetCardByExternalId(3, It.IsAny<string>())).Returns((Card)null); MockLeanKitApi.Setup(x => x.AddCard(3, It.IsAny<Card>(), It.IsAny<string>())).Returns(_testCardAddResult1); MockRestClient.Setup(x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("/4/issues") && y.Method == Method.GET && y.Parameters[0].Value.ToString() == "Open"))).Returns(restResponse1); MockRestClient.Setup(x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("/4/issues") && y.Method == Method.GET && y.Parameters[0].Value.ToString() != "Open"))).Returns(noRestResponse); MockLeanKitApi.Setup(x => x.GetCardByExternalId(4, It.IsAny<string>())).Returns(new Card() { Id = 4, ExternalSystemName = "GitHub"}); MockLeanKitApi.Setup(x => x.AddCard(4, It.IsAny<Card>(), It.IsAny<string>())).Returns(_testCardAddResult1); MockRestClient.Setup(x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("/5/issues") && y.Method == Method.GET && y.Parameters[0].Value.ToString() == "Open"))).Returns(restResponse1); MockRestClient.Setup(x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("/5/issues") && y.Method == Method.GET && y.Parameters[0].Value.ToString() != "Open"))).Returns(noRestResponse); MockLeanKitApi.Setup(x => x.GetCardByExternalId(5, It.IsAny<string>())).Returns(new Card() { Id = 4, ExternalSystemName = "GitHubby" }); MockLeanKitApi.Setup(x => x.AddCard(5, It.IsAny<Card>(), It.IsAny<string>())).Returns(_testCardAddResult1); }
public long? CalculateAssignedUserId(long boardId, Ticket ticket) { if (ticket == null) return null; if (ticket.Assignee_Id > 0) { // http://mysubdomain.unfuddle.com/api/v1/people/{id} var request = new RestRequest(string.Format("/api/v1/people/{0}", ticket.Assignee_Id), Method.GET); var unfuddleResp = _restClient.Execute(request); if (unfuddleResp.StatusCode != HttpStatusCode.OK) { var serializer = new JsonSerializer<ErrorMessage>(); var errorMessage = serializer.DeserializeFromString(unfuddleResp.Content); Log.Warn(string.Format("Unable to get user from Unfuddle, Error: {0}", errorMessage.Message)); } else { var user = new JsonSerializer<Person>().DeserializeFromString(unfuddleResp.Content); if (user != null) { var lkUser = LeanKit.GetBoard(boardId).BoardUsers.FirstOrDefault(x => x!= null && ((!string.IsNullOrEmpty(x.EmailAddress)) && (!string.IsNullOrEmpty(user.Email)) && x.EmailAddress.ToLowerInvariant() == user.Email.ToLowerInvariant()) || ((!string.IsNullOrEmpty(x.FullName)) && (!string.IsNullOrEmpty(user.Last_Name)) && x.FullName.ToLowerInvariant() == (user.First_Name + " " + user.Last_Name).ToLowerInvariant()) || ((!string.IsNullOrEmpty(x.UserName)) && (!string.IsNullOrEmpty(user.Username)) && x.UserName.ToLowerInvariant() == user.Username.ToLowerInvariant())); if (lkUser != null) return lkUser.Id; } } } return null; }
protected void UpdateStateOfExternalItem(Card card, List<string> states, BoardMapping mapping, bool runOnlyOnce) { if (!card.ExternalSystemName.Equals(ServiceName, StringComparison.OrdinalIgnoreCase)) return; if (states == null || states.Count == 0) return; if (string.IsNullOrEmpty(card.ExternalCardID)) { Log.Debug("Ignoring card [{0}] with missing external id value.", card.Id); return; } int tries = 0; bool success = false; while (tries < 10 && !success && (!runOnlyOnce || tries == 0)) { if (tries > 0) { Log.Error(string.Format("Attempting to update external ticket [{0}] attempt number [{1}]", card.ExternalCardID, tries)); // wait 5 seconds before trying again Thread.Sleep(new TimeSpan(0, 0, 5)); } //https://mysubdomain.unfuddle.com/api/v1/projects/{id}/tickets/{id} var request = new RestRequest(string.Format("/api/v1/projects/{0}/tickets/{1}", mapping.Identity.Target, card.ExternalCardID), Method.GET); var unfuddleResp = _restClient.Execute(request); if (unfuddleResp.StatusCode != HttpStatusCode.OK) { var serializer = new JsonSerializer<ErrorMessage>(); var errorMessage = serializer.DeserializeFromString(unfuddleResp.Content); Log.Error(string.Format("Unable to get tickets from Unfuddle, Error: {0}. Check your board/repo mapping configuration.", errorMessage.Message)); } else { var ticketToUpdate = new JsonSerializer<Ticket>().DeserializeFromString(unfuddleResp.Content); if (ticketToUpdate != null && ticketToUpdate.Id.ToString() == card.ExternalCardID) { // Check for a workflow mapping to the closed state if (states[0].Contains(">")) { var workflowStates = states[0].Split('>'); // check to see if the workitem is already in one of the workflow states var alreadyInState = workflowStates.FirstOrDefault(x => x.Trim().ToLowerInvariant() == ticketToUpdate.Status.ToLowerInvariant()); if (!string.IsNullOrEmpty(alreadyInState)) { // change workflowStates to only use the states after the currently set state var currentIndex = Array.IndexOf(workflowStates, alreadyInState); if (currentIndex < workflowStates.Length - 1) { var updatedWorkflowStates = new List<string>(); for (int i = currentIndex + 1; i < workflowStates.Length; i++) { updatedWorkflowStates.Add(workflowStates[i]); } workflowStates = updatedWorkflowStates.ToArray(); } } if (workflowStates.Length > 0) { foreach (string workflowState in workflowStates) { //UpdateStateOfExternalItem(card, new LaneStateMap() { State = workflowState.Trim(), States = new List<string>() { workflowState.Trim() } }, mapping, runOnlyOnce); UpdateStateOfExternalItem(card, new List<string> {workflowState.Trim()}, mapping, runOnlyOnce); } return; } } if (ticketToUpdate.Status.ToLowerInvariant() == states[0].ToLowerInvariant()) { Log.Debug(string.Format("Ticket [{0}] is already in state [{1}]", ticketToUpdate.Id, states[0])); return; } ticketToUpdate.Status = states[0]; try { //https://mysubdomain.unfuddle.com/api/v1/api/v1/projects/{id}/tickets/{id} var updateRequest = new RestRequest(string.Format("/api/v1/projects/{0}/tickets/{1}", mapping.Identity.Target, card.ExternalCardID), Method.PUT); updateRequest.AddHeader("Accept", "application/json"); updateRequest.AddHeader("Content-type", "application/xml"); updateRequest.AddParameter("application/xml", "<ticket><status>" + ticketToUpdate.Status + "</status></ticket>", ParameterType.RequestBody); var resp = _restClient.Execute(updateRequest); if (resp.StatusCode != HttpStatusCode.OK) { if (resp.Content != null && !string.IsNullOrEmpty(resp.Content.Trim())) { var serializer = new JsonSerializer<ErrorMessage>(); var errorMessage = serializer.DeserializeFromString(resp.Content); Log.Error(string.Format("Unable to update Ticket [{0}] to [{1}], Description: {2}, Message: {3}", card.ExternalCardID, ticketToUpdate.Status, resp.StatusDescription, errorMessage.Message)); } else { Log.Error(string.Format("Unable to update Ticket [{0}] to [{1}], Description: {2}, Message: {3}", card.ExternalCardID, ticketToUpdate.Status, resp.StatusDescription, resp.StatusDescription)); } } else { success = true; Log.Debug(String.Format("Updated state for Ticket [{0}] to [{1}]", card.ExternalCardID, ticketToUpdate.Status)); } } catch (Exception ex) { Log.Error(string.Format("Unable to update Ticket [{0}] to [{1}], Exception: {2}", card.ExternalCardID, ticketToUpdate.Status, ex.Message)); } } else { Log.Debug(String.Format("Could not retrieve Ticket [{0}] for updating state to [{1}]", card.ExternalCardID, ticketToUpdate.Status)); } } tries++; } }
protected override void Synchronize(BoardMapping project) { Log.Debug("Polling Unfuddle for Tickets"); var queryAsOfDate = QueryDate.AddMilliseconds(Configuration.PollingFrequency * -1.5); var unfuddleQuery = !string.IsNullOrEmpty(project.Query) ? string.Format(project.Query, queryAsOfDate.ToString("yyyy/MM/dd hh:mm")) : string.Format("status-eq-{0},created_at-gt-{1}", project.QueryStates[0], queryAsOfDate.ToString("yyyy/MM/dd hh:mm")); //http://mysubdomain.unfuddle.com/api/v1/projects/{id}/ticket_reports/dynamic?sort_by=created_at&sort_direction=ASC&conditions_string=status-eq-new,created_at-gt-yyyy/MM/dd hh:mm var request = new RestRequest(string.Format("/api/v1/projects/{0}/ticket_reports/dynamic", project.Identity.Target), Method.GET); request.AddParameter("sort_by", "created_at"); request.AddParameter("sort_direction", "ASC"); request.AddParameter("conditions_string", unfuddleQuery); //, "id,status,priority,summary,description,type"); request.AddParameter("limit", "500"); var unfuddleResp = _restClient.Execute(request); if (unfuddleResp.StatusCode != HttpStatusCode.OK) { var serializer = new JsonSerializer<ErrorMessage>(); var errorMessage = serializer.DeserializeFromString(unfuddleResp.Content); Log.Error(string.Format("Unable to get tickets from Unfuddle, Error: {0}. Check your board/project mapping configuration.", errorMessage.Message)); return; } var resp = new JsonSerializer<TicketsResponse>().DeserializeFromString(unfuddleResp.Content); Log.Info("\nQueried [{0}] at {1} for changes after {2}", project.Identity.Target, QueryDate, queryAsOfDate.ToString("o")); if (resp != null && resp.Groups != null && resp.Groups.Any()) { foreach (var group in resp.Groups) { if (group != null && group.Tickets != null && group.Tickets.Any()) { var tickets = group.Tickets; foreach (var ticket in tickets) { Log.Info("Ticket [{0}]: {1}, {2}, {3}", ticket.Id, ticket.Summary, ticket.Status, ticket.Priority); // does this workitem have a corresponding card? var card = LeanKit.GetCardByExternalId(project.Identity.LeanKit, ticket.Id.ToString()); if (card == null || !card.ExternalSystemName.Equals(ServiceName, StringComparison.OrdinalIgnoreCase)) { Log.Debug("Create new card for Ticket [{0}]", ticket.Id); CreateCardFromItem(project, ticket); } else { Log.Debug("Previously created a card for Ticket [{0}]", ticket.Id); if (project.UpdateCards) TicketUpdated(ticket, card, project); else Log.Info("Skipped card update because 'UpdateCards' is disabled."); } } } } Log.Info("{0} item(s) queried.\n", resp.Count); } }
protected override void OnArrange() { base.OnArrange(); MockLeanKitApi.Setup(x => x.GetBoard(It.IsAny<long>())).Returns(_testBoard); var serializer = new JsonSerializer<Jira.Issue>(); var issue1 = new Jira.Issue() { Id = 1, Key = "one", Fields = new Jira.Fields() {Status = new Jira.Status() {Name = "Open"}} }; var restResponse1 = new RestResponse() { Content = serializer.SerializeToString(issue1), StatusCode = HttpStatusCode.OK }; var transitions1 = new Jira.TransitionsResponse() { Transitions = new List<Jira.Transition>() { new Jira.Transition() { Id = "2", Name = "Closed", To = new Jira.Status() {Name = "Closed", Description = "Closed", Id = "2"} } } }; var transitionsSerializer = new JsonSerializer<Jira.TransitionsResponse>(); var restTransitionsResponse1 = new RestResponse() { Content = transitionsSerializer.SerializeToString(transitions1), StatusCode = HttpStatusCode.OK }; MockRestClient.Setup( x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("latest/issue/one") && y.Method == Method.GET))) .Returns(restResponse1); MockRestClient.Setup( x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("2/issue/one/transitions") && y.Method == Method.GET))) .Returns(restTransitionsResponse1); MockRestClient.Setup( x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("issue/one/transitions") && y.Method == Method.POST))) .Returns(restResponse1); var issue2 = new Jira.Issue() { Id = 2, Key = "two", Fields = new Jira.Fields() {Status = new Jira.Status() {Name = "Closed"}} }; var restResponse2 = new RestResponse() { Content = serializer.SerializeToString(issue2), StatusCode = HttpStatusCode.OK }; MockRestClient.Setup( x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("latest/issue/two") && y.Method == Method.GET))) .Returns(restResponse2); MockRestClient.Setup( x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("2/issue/two/transitions") && y.Method == Method.GET))) .Returns(restTransitionsResponse1); MockRestClient.Setup( x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("issue/two/transitions") && y.Method == Method.POST))) .Returns(restResponse2); var errorSerializer = new JsonSerializer<Jira.ErrorMessage>(); var errorResponse = new RestResponse() { Content = errorSerializer.SerializeToString(new Jira.ErrorMessage() {Message = "Error"}), StatusCode = HttpStatusCode.NotFound }; MockRestClient.Setup( x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("latest/issue/three") && y.Method == Method.GET))) .Returns(errorResponse); MockRestClient.Setup( x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("2/issue/three/transitions") && y.Method == Method.GET))) .Returns(restTransitionsResponse1); MockRestClient.Setup( x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("issue/three/transitions") && y.Method == Method.POST))) .Returns(errorResponse); var issue4 = new Jira.Issue() { Id = 4, Key = "four", Fields = new Jira.Fields() {Status = new Jira.Status() {Name = "Open"}} }; var restResponse4 = new RestResponse() { Content = serializer.SerializeToString(issue4), StatusCode = HttpStatusCode.OK }; MockRestClient.Setup( x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("latest/issue/four") && y.Method == Method.GET))) .Returns(restResponse4); MockRestClient.Setup( x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("2/issue/four/transitions") && y.Method == Method.GET))) .Returns(errorResponse); MockRestClient.Setup( x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("issue/four/transitions") && y.Method == Method.POST))) .Returns(restResponse4); var transitions2 = new Jira.TransitionsResponse() { Transitions = new List<Jira.Transition>() { new Jira.Transition() { Id = "3", Name = "Resolved", To = new Jira.Status() {Name = "Resolved", Description = "Resolved", Id = "3"} } } }; var restTransitionsResponse2 = new RestResponse() { Content = transitionsSerializer.SerializeToString(transitions2), StatusCode = HttpStatusCode.OK }; var issue5 = new Jira.Issue() { Id = 5, Key = "five", Fields = new Jira.Fields() {Status = new Jira.Status() {Name = "Open"}} }; var restResponse5 = new RestResponse() { Content = serializer.SerializeToString(issue5), StatusCode = HttpStatusCode.OK }; MockRestClient.Setup( x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("latest/issue/five") && y.Method == Method.GET))) .Returns(restResponse5); MockRestClient.Setup( x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("2/issue/five/transitions") && y.Method == Method.GET))) .Returns(restTransitionsResponse2); MockRestClient.Setup( x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("issue/five/transitions") && y.Method == Method.POST))) .Returns(restResponse5); }
protected override void OnArrange() { base.OnArrange(); MockLeanKitApi.Setup(x => x.GetBoard(It.IsAny<long>())).Returns(_testBoard); var serializer = new JsonSerializer<Jira.Issue>(); var issue1 = new Jira.Issue() { Id = 1, Key = "one", Fields = new Jira.Fields() { Status = new Jira.Status() {Name = "Open"}, Description = "Issue 1", Summary = "Issue 1" } }; var restResponse1 = new RestResponse() { Content = serializer.SerializeToString(issue1), StatusCode = HttpStatusCode.OK }; MockRestClient.Setup( x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("latest/issue/one") && y.Method == Method.GET))) .Returns(restResponse1); MockRestClient.Setup( x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("latest/issue/one") && y.Method == Method.PUT))) .Returns(restResponse1); var issue2 = new Jira.Issue() { Id = 2, Key = "two", Fields = new Jira.Fields() { Status = new Jira.Status() {Name = "Open"}, Description = "Issue 2", Summary = "Issue 2" } }; var restResponse2 = new RestResponse() { Content = serializer.SerializeToString(issue2), StatusCode = HttpStatusCode.OK }; MockRestClient.Setup( x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("latest/issue/two") && y.Method == Method.GET))) .Returns(restResponse2); MockRestClient.Setup( x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("latest/issue/two") && y.Method == Method.PUT))) .Returns(restResponse2); var issue3 = new Jira.Issue() { Id = 3, Key = "three", Fields = new Jira.Fields() { Status = new Jira.Status() {Name = "Open"}, Description = "Issue 3", Summary = "Issue 3" } }; var restResponse3 = new RestResponse() { Content = serializer.SerializeToString(issue3), StatusCode = HttpStatusCode.OK }; MockRestClient.Setup( x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("latest/issue/three") && y.Method == Method.GET))) .Returns(restResponse3); MockRestClient.Setup( x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("latest/issue/three") && y.Method == Method.PUT))) .Returns(restResponse3); var issue4 = new Jira.Issue() { Id = 4, Key = "four", Fields = new Jira.Fields() { Status = new Jira.Status() {Name = "Open"}, Description = "Issue 4", Summary = "Issue 4" } }; var restResponse4 = new RestResponse() { Content = serializer.SerializeToString(issue4), StatusCode = HttpStatusCode.OK }; MockRestClient.Setup( x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("latest/issue/four") && y.Method == Method.GET))) .Returns(restResponse4); MockRestClient.Setup( x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("latest/issue/four") && y.Method == Method.PUT))) .Returns(restResponse4); }
protected override void CreateNewItem(Card card, BoardMapping boardMapping) { var jiraIssueType = GetJiraIssueType(boardMapping, card.TypeId); string json = "{ \"fields\": { "; json += "\"project\": { \"key\": \"" + boardMapping.Identity.Target + "\" }"; json += ", \"summary\": \"" + card.Title.Replace("\"", "\\\"") + "\" "; json += ", \"description\": \"" + card.Description.LeanKitHtmlToJiraPlainText() + "\" "; json += ", \"issuetype\": { \"name\": \"" + jiraIssueType + "\" }"; json += ", \"priority\": { \"name\": \"" + GetPriority(card.Priority) + "\" }"; if (jiraIssueType.ToLowerInvariant() == "epic") { if (CustomFields.Any()) { var epicNameField = CustomFields.FirstOrDefault(x => x.Name == "Epic Name"); if (epicNameField != null) { json += ", \"" + epicNameField.Id + "\": \"" + card.Title.Replace("\"", "\\\"") + "\""; } } } if (!string.IsNullOrEmpty(card.DueDate) && CurrentUser != null) { try { var dateFormat = CurrentUser.DateFormat ?? "MM/dd/yyyy"; var parsed = DateTime.ParseExact(card.DueDate, dateFormat, CultureInfo.InvariantCulture); json += ", \"duedate\": \"" + parsed.ToString("o") + "\""; } catch (Exception ex) { Log.Warn(ex, "Could not parse due date: {0}", card.DueDate); } } if (!string.IsNullOrEmpty(card.Tags)) { var newLabels = card.Tags.Split(','); string updateLabels = ""; int ctr = 0; foreach (string newLabel in newLabels) { if (ctr > 0) updateLabels += ", "; updateLabels += "\"" + newLabel.Trim() + "\""; ctr++; } json += ", \"labels\": [" + updateLabels + "]"; } json += "}}"; Issue newIssue = null; try { //https://yoursite.atlassian.net/rest/api/latest/issue var createRequest = CreateRequest("rest/api/latest/issue", Method.POST); createRequest.AddParameter("application/json", json, ParameterType.RequestBody); var resp = ExecuteRequest(createRequest); if (resp.StatusCode != HttpStatusCode.OK && resp.StatusCode != HttpStatusCode.Created) { Log.Error(string.Format("Unable to create Issue from card [{0}], Description: {1}, Message: {2}", card.ExternalCardID, resp.StatusDescription, resp.Content)); } else { newIssue = new JsonSerializer<Issue>().DeserializeFromString(resp.Content); Log.Debug(String.Format("Created Issue [{0}]", newIssue.Key)); } } catch (Exception ex) { Log.Error(string.Format("Unable to create Issue from Card [{0}], Exception: {1}", card.ExternalCardID, ex.Message)); } if (newIssue != null) { try { card.ExternalCardID = newIssue.Key; card.ExternalSystemName = ServiceName; card.ExternalSystemUrl = string.Format(_externalUrlTemplate, newIssue.Key); // now that we've created the work item let's try to set it to any matching state defined by lane var states = boardMapping.LaneToStatesMap[card.LaneId]; if (states != null) { UpdateStateOfExternalItem(card, states, boardMapping, true); } LeanKit.UpdateCard(boardMapping.Identity.LeanKit, card); } catch (Exception ex) { Log.Error(string.Format("Error updating Card [{0}] after creating new Issue, Exception: {1}", card.ExternalCardID, ex.Message)); } } }
protected override void CardUpdated(Card updatedCard, List<string> updatedItems, BoardMapping boardMapping) { if (!updatedCard.ExternalSystemName.Equals(ServiceName, StringComparison.OrdinalIgnoreCase)) return; if (string.IsNullOrEmpty(updatedCard.ExternalCardID)) return; long issueNumber; string target = boardMapping.Identity.Target; // use external card id to get the GitHub Issue try { issueNumber = Convert.ToInt32(updatedCard.ExternalCardID.Split('|')[1]); } catch (Exception) { Log.Debug("Ignoring card [{0}] with missing external id value.", updatedCard.Id); return; } //"https://api.github.com/repos/{0}/{1}/pulls/{2} var request = new RestRequest(string.Format("repos/{0}/{1}/pulls/{2}", Configuration.Target.Host, target, issueNumber), Method.GET); var ghResp = _restClient.Execute(request); if (ghResp.StatusCode != HttpStatusCode.OK) { var serializer = new JsonSerializer<ErrorMessage>(); var errorMessage = serializer.DeserializeFromString(ghResp.Content); Log.Error(string.Format("Unable to get Pull Request from GitHub, Error: {0}. Check your board mapping configuration.", errorMessage.Message)); } else { var pullToUpdate = new JsonSerializer<Pull>().DeserializeFromString(ghResp.Content); if (pullToUpdate != null && pullToUpdate.Number == issueNumber) { bool isDirty = false; if (updatedItems.Contains("Title") && pullToUpdate.Title != updatedCard.Title) { pullToUpdate.Title = updatedCard.Title.Replace("\"", "\\\""); isDirty = true; } if (updatedItems.Contains("Description") && pullToUpdate.Body.SanitizeCardDescription() != updatedCard.Description) { pullToUpdate.Body = updatedCard.Description; isDirty = true; } // Do nothing with Priority, DueDate, Size, Blocked, or Tags because GitHub pull requests do not have comments //if (updatedItems.Contains("Priority")) //if (updatedItems.Contains("DueDate")) //if (updatedItems.Contains("Size")) //if (updatedItems.Contains("Blocked")) //if (updatedItems.Contains("Tags")) if (isDirty) { try { //"https://api.github.com/repos/{0}/{1}/pulls/{2} var updateRequest = new RestRequest(string.Format("repos/{0}/{1}/pulls/{2}", Configuration.Target.Host, target, issueNumber), Method.PATCH); updateRequest.AddParameter( "application/json", "{ \"title\": \"" + pullToUpdate.Title + "\", \"body\": \"" + pullToUpdate.Body + "\"}", ParameterType.RequestBody ); var resp = _restClient.Execute(updateRequest); if (resp.StatusCode != HttpStatusCode.OK) { var serializer = new JsonSerializer<ErrorMessage>(); var errorMessage = serializer.DeserializeFromString(resp.Content); Log.Error(string.Format("Unable to update Pull Request [{0}], Description: {1}, Message: {2}", issueNumber, resp.StatusDescription, errorMessage.Message)); } else { Log.Debug(String.Format("Updated Pull Request [{0}]", issueNumber)); } } catch (Exception ex) { Log.Error(string.Format("Unable to update Pull Request [{0}], Exception: {1}", issueNumber, ex.Message)); } } } } }
protected override void OnArrange() { base.OnArrange(); MockLeanKitApi.Setup(x => x.GetBoard(It.IsAny<long>())).Returns(_testBoard); var serializer = new JsonSerializer<Unfuddle.Person>(); var user1 = new Unfuddle.Person() { Email = "*****@*****.**", First_Name = "Johnny", Id = 1, Last_Name = "Cash", Username = "******" }; var user2 = new Unfuddle.Person() { Email = "*****@*****.**", First_Name = "Willy", Id = 2, Last_Name = "Cash", Username = "******" }; var user3 = new Unfuddle.Person() { Email = "", First_Name = "", Id = 3, Last_Name = "", Username = "" }; var restResponse1 = new RestResponse() { Content = serializer.SerializeToString(user1), StatusCode = HttpStatusCode.OK }; var restResponse2 = new RestResponse() { Content = serializer.SerializeToString(user2), StatusCode = HttpStatusCode.OK }; var restResponse3 = new RestResponse() { Content = serializer.SerializeToString(user3), StatusCode = HttpStatusCode.OK }; MockRestClient.Setup(x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("people/1")))).Returns(restResponse1); MockRestClient.Setup(x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("people/2")))).Returns(restResponse2); MockRestClient.Setup(x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("people/3")))).Returns(restResponse3); }
protected override void Synchronize(BoardMapping project) { Log.Debug("Polling GitHub for Issues"); var queryAsOfDate = QueryDate.AddMilliseconds(Configuration.PollingFrequency * -1.5); // GitHub will only let us query one state at a time :( foreach (var state in project.QueryStates) { //https://api.github.com/repos/{0}/{1}/issues?state=Open&since={2} var request = new RestRequest(string.Format("repos/{0}/{1}/issues", Configuration.Target.Host, project.Identity.Target), Method.GET); request.AddParameter("state", state); request.AddParameter("since", queryAsOfDate.ToString("o")); var resp = _restClient.Execute(request); if (resp.StatusCode != HttpStatusCode.OK) { var serializer = new JsonSerializer<ErrorMessage>(); var errorMessage = serializer.DeserializeFromString(resp.Content); Log.Error(string.Format("Unable to get issues from GitHub, Error: {0}. Check your board/repo mapping configuration.", errorMessage.Message)); return; } var issues = new JsonSerializer<List<Issue>>().DeserializeFromString(resp.Content); Log.Info("\nQueried [{0}] at {1} for changes after {2}", project.Identity.Target, QueryDate, queryAsOfDate.ToString("o")); if (issues == null || !issues.Any() || issues[0].Id <= 0) continue; foreach (var issue in issues.Where(issue => issue.Id > 0)) { Log.Info("Issue [{0}]: {1}, {2}, {3}", issue.Number, issue.Title, issue.User.Login, issue.State); // does this workitem have a corresponding card? var card = LeanKit.GetCardByExternalId(project.Identity.LeanKit, issue.Id + "|" + issue.Number.ToString()); if (card == null || !card.ExternalSystemName.Equals(ServiceName, StringComparison.OrdinalIgnoreCase)) { Log.Debug("Create new card for Issue [{0}]", issue.Number); CreateCardFromItem(project, issue); } else { Log.Debug("Previously created a card for Issue [{0}]", issue.Number); if (project.UpdateCards) IssueUpdated(issue, card, project); else Log.Info("Skipped card update because 'UpdateCards' is disabled."); } } Log.Info("{0} item(s) queried.\n", issues.Count); } }
protected override void OnArrange() { base.OnArrange(); MockLeanKitApi.Setup(x => x.GetBoard(It.IsAny<long>())).Returns(_testBoard); var serializer = new JsonSerializer<Unfuddle.Ticket>(); var ticket1 = new Unfuddle.Ticket() { Id = 1, Status = "Open" }; var restResponse1 = new RestResponse() { Content = serializer.SerializeToString(ticket1), StatusCode = HttpStatusCode.OK }; MockRestClient.Setup(x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("tickets/1") && y.Method == Method.GET))).Returns(restResponse1); MockRestClient.Setup(x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("tickets/1") && y.Method == Method.PUT))).Returns(restResponse1); var ticket2 = new Unfuddle.Ticket() { Id = 2, Status = "Accepted" }; var restResponse2 = new RestResponse() { Content = serializer.SerializeToString(ticket2), StatusCode = HttpStatusCode.OK }; MockRestClient.Setup(x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("tickets/2") && y.Method == Method.GET))).Returns(restResponse2); MockRestClient.Setup(x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("tickets/2") && y.Method == Method.PUT))).Returns(restResponse2); var ticket3 = new Unfuddle.Ticket() { Id = 3, Status = "Open" }; var restResponse3 = new RestResponse() { Content = serializer.SerializeToString(ticket3), StatusCode = HttpStatusCode.OK }; MockRestClient.Setup(x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("tickets/3") && y.Method == Method.GET))).Returns(restResponse3); MockRestClient.Setup(x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("tickets/3") && y.Method == Method.PUT))).Returns(restResponse3); var ticket4 = new Unfuddle.Ticket() { Id = 4, Status = "Resolved" }; var restResponse4 = new RestResponse() { Content = serializer.SerializeToString(ticket4), StatusCode = HttpStatusCode.OK }; MockRestClient.Setup(x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("tickets/4") && y.Method == Method.GET))).Returns(restResponse4); MockRestClient.Setup(x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("tickets/4") && y.Method == Method.PUT))).Returns(restResponse4); }
protected void UpdateStateOfExternalItem(Card card, List<string> states, BoardMapping boardMapping, bool runOnlyOnce) { if (!card.ExternalSystemName.Equals(ServiceName, StringComparison.OrdinalIgnoreCase)) return; if (string.IsNullOrEmpty(card.ExternalCardID)) return; if (states == null || states.Count == 0) return; long issueNumber; string target = boardMapping.Identity.Target; // use external card id to get the GitHub issue try { issueNumber = Convert.ToInt32(card.ExternalCardID.Split('|')[1]); } catch (Exception) { Log.Debug("Ignoring card [{0}] with missing external id value.", card.Id); return; } int tries = 0; bool success = false; while (tries < 10 && !success && (!runOnlyOnce || tries == 0)) { if (tries > 0) { Log.Error(string.Format("Attempting to update external issue [{0}] attempt number [{1}]",issueNumber, tries)); // wait 5 seconds before trying again Thread.Sleep(new TimeSpan(0, 0, 5)); } //"https://api.github.com/repos/{0}/{1}/issues/{2} var request = new RestRequest(string.Format("repos/{0}/{1}/issues/{2}", Configuration.Target.Host, target, issueNumber), Method.GET); var ghResp = _restClient.Execute(request); if (ghResp.StatusCode != HttpStatusCode.OK) { var serializer = new JsonSerializer<ErrorMessage>(); var errorMessage = serializer.DeserializeFromString(ghResp.Content); Log.Error(string.Format("Unable to get issue from GitHub, Error: {0}. Check your board mapping configuration.", errorMessage.Message)); } else { var issueToUpdate = new JsonSerializer<Issue>().DeserializeFromString(ghResp.Content); if (issueToUpdate != null && issueToUpdate.Number == issueNumber) { if (issueToUpdate.State.ToLowerInvariant() == states[0].ToLowerInvariant()) { Log.Debug(string.Format("Issue [{0}] is already in state [{1}]", issueToUpdate.Id, states[0])); return; } issueToUpdate.State = states[0]; try { //"https://api.github.com/repos/{0}/{1}/issues/{2} var updateRequest = new RestRequest(string.Format("repos/{0}/{1}/issues/{2}", Configuration.Target.Host, target, issueNumber), Method.PATCH); updateRequest.AddParameter("application/json", "{ \"state\": \"" + issueToUpdate.State + "\"}", ParameterType.RequestBody); var resp = _restClient.Execute(updateRequest); if (resp.StatusCode != HttpStatusCode.OK) { var serializer = new JsonSerializer<ErrorMessage>(); var errorMessage = serializer.DeserializeFromString(resp.Content); Log.Error(string.Format("Unable to update Issue [{0}] to [{1}], Description: {2}, Message: {3}", issueNumber, issueToUpdate.State, resp.StatusDescription, errorMessage.Message)); } else { success = true; Log.Debug(String.Format("Updated state for Issue [{0}] to [{1}]", issueNumber, issueToUpdate.State)); } } catch (Exception ex) { Log.Error(string.Format("Unable to update Issue [{0}] to [{1}], Exception: {2}", issueNumber, issueToUpdate.State, ex.Message)); } } else { Log.Debug(String.Format("Could not retrieve Issue [{0}] for updating state to [{1}]", issueNumber, issueToUpdate.State)); } } tries++; } }
protected override void OnArrange() { base.OnArrange(); MockLeanKitApi.Setup(x => x.GetBoard(It.IsAny<long>())).Returns(_testBoard); var serializer = new JsonSerializer<GitHubPulls.Pull>(); var pull1 = new GitHubPulls.Pull() { Id = 1, Number = 1, Title = "Pull 1", Body = "Pull 1", State = "Open" }; var restResponse1 = new RestResponse() { Content = serializer.SerializeToString(pull1), StatusCode = HttpStatusCode.OK }; MockRestClient.Setup( x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("pulls/1") && y.Method == Method.GET))) .Returns(restResponse1); MockRestClient.Setup( x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("pulls/1") && y.Method == Method.PATCH))) .Returns(restResponse1); var pull2 = new GitHubPulls.Pull() { Id = 2, Number = 2, Title = "Pull 2", Body = "Pull 2", State = "Open" }; var restResponse2 = new RestResponse() { Content = serializer.SerializeToString(pull2), StatusCode = HttpStatusCode.OK }; MockRestClient.Setup( x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("pulls/2") && y.Method == Method.GET))) .Returns(restResponse2); MockRestClient.Setup( x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("pulls/2") && y.Method == Method.PATCH))) .Returns(restResponse2); var pull3 = new GitHubPulls.Pull() { Id = 3, Number = 3, Title = "Pull 3", Body = "Pull 3", State = "Open" }; var restResponse3 = new RestResponse() { Content = serializer.SerializeToString(pull3), StatusCode = HttpStatusCode.OK }; MockRestClient.Setup( x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("pulls/3") && y.Method == Method.GET))) .Returns(restResponse3); MockRestClient.Setup( x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("pulls/3") && y.Method == Method.PATCH))) .Returns(restResponse3); var pull4 = new GitHubPulls.Pull() { Id = 4, Number = 4, Title = "Pull 4", Body = "Pull 4", State = "Open" }; var restResponse4 = new RestResponse() { Content = serializer.SerializeToString(pull4), StatusCode = HttpStatusCode.OK }; MockRestClient.Setup( x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("pulls/4") && y.Method == Method.GET))) .Returns(restResponse4); MockRestClient.Setup( x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("pulls/4") && y.Method == Method.PATCH))) .Returns(restResponse4); }
protected override void CardUpdated(Card updatedCard, List<string> updatedItems, BoardMapping boardMapping) { if (!updatedCard.ExternalSystemName.Equals(ServiceName, StringComparison.OrdinalIgnoreCase)) return; long issueNumber; string target = boardMapping.Identity.Target; // use external card id to get the GitHub Issue try { issueNumber = Convert.ToInt32(updatedCard.ExternalCardID.Split('|')[1]); } catch (Exception) { Log.Debug("Ignoring card [{0}] with missing external id value.", updatedCard.Id); return; } //"https://api.github.com/repos/{0}/{1}/issues/{2} var request = new RestRequest(string.Format("repos/{0}/{1}/issues/{2}", Configuration.Target.Host, target, issueNumber), Method.GET); var ghResp = _restClient.Execute(request); if (ghResp.StatusCode != HttpStatusCode.OK) { var serializer = new JsonSerializer<ErrorMessage>(); var errorMessage = serializer.DeserializeFromString(ghResp.Content); Log.Error(string.Format("Unable to get issue from GitHub, Error: {0}. Check your board mapping configuration.", errorMessage.Message)); } else { var issueToUpdate = new JsonSerializer<Issue>().DeserializeFromString(ghResp.Content); if (issueToUpdate != null && issueToUpdate.Number == issueNumber) { bool isDirty = false; if (updatedItems.Contains("Title") && issueToUpdate.Title != updatedCard.Title) { issueToUpdate.Title = updatedCard.Title; isDirty = true; } string updateJson = "{ \"title\": \"" + issueToUpdate.Title.Replace("\"", "\\\"") + "\""; if (updatedItems.Contains("Description") && issueToUpdate.Body.SanitizeCardDescription() != updatedCard.Description) { updateJson += ", \"body\": \"" + updatedCard.Description.Replace("\"", "\\\"") + "\""; isDirty = true; } if (updatedItems.Contains("Tags")) { var newLabels = updatedCard.Tags.Split(','); string updateLabels = ""; int ctr = 0; foreach (string newLabel in newLabels) { if (ctr > 0) updateLabels += ", "; updateLabels += "{ \"name\": \"" + newLabel.Trim() + "\"}"; ctr++; } updateJson += ", \"labels\": [" + updateLabels + "]"; isDirty = true; } updateJson += "}"; string comment = ""; if (updatedItems.Contains("Priority")) { comment += "LeanKit card Priority changed to " + updatedCard.Priority + ".<br />"; } if (updatedItems.Contains("DueDate")) { comment += "LeanKit card DueDate changed to " + updatedCard.DueDate + ".<br />"; } if (updatedItems.Contains("Size")) { comment += "LeanKit card Size changed to " + updatedCard.Size + ".<br />"; } if (updatedItems.Contains("Blocked")) { if (updatedCard.IsBlocked) comment += "LeanKit card is blocked: " + updatedCard.BlockReason + ".<br />"; else comment += "LeanKit card is no longer blocked: " + updatedCard.BlockReason + ".<br />"; } if (isDirty) { try { //"https://api.github.com/repos/{0}/{1}/issues/{2} var updateRequest = new RestRequest(string.Format("repos/{0}/{1}/issues/{2}", Configuration.Target.Host, target, issueNumber), Method.PATCH); updateRequest.AddParameter( "application/json", updateJson, ParameterType.RequestBody ); var resp = _restClient.Execute(updateRequest); if (resp.StatusCode != HttpStatusCode.OK) { var serializer = new JsonSerializer<ErrorMessage>(); var errorMessage = serializer.DeserializeFromString(resp.Content); Log.Error(string.Format("Unable to update Issue [{0}], Description: {1}, Message: {2}", issueNumber, resp.StatusDescription, errorMessage.Message)); } else { Log.Debug(String.Format("Updated Issue [{0}]", issueNumber)); } } catch (Exception ex) { Log.Error(string.Format("Unable to update Issue [{0}], Exception: {1}", issueNumber, ex.Message)); } } if (!string.IsNullOrEmpty(comment)) { try { //"https://api.github.com/repos/{0}/{1}/issues/{2}/comments var newCommentRequest = new RestRequest(string.Format("repos/{0}/{1}/issues/{2}/comments", Configuration.Target.Host, target, issueNumber), Method.POST); newCommentRequest.AddParameter( "application/json", "{ \"body\": \"" + comment + "\"}", ParameterType.RequestBody ); var resp = _restClient.Execute(newCommentRequest); if (resp.StatusCode != HttpStatusCode.OK || resp.StatusCode != HttpStatusCode.Created) { var serializer = new JsonSerializer<ErrorMessage>(); var errorMessage = serializer.DeserializeFromString(resp.Content); Log.Error(string.Format("Unable to create comment on updated Issue [{0}], Description: {1}, Message: {2}", issueNumber, resp.StatusDescription, errorMessage.Message)); } else { Log.Debug(String.Format("Created comment on Updated Issue [{0}]", issueNumber)); } } catch (Exception ex) { Log.Error(string.Format("Unable to create comment on updated Issue [{0}], Exception: {1}", issueNumber, ex.Message)); } } } } }
protected override void OnArrange() { base.OnArrange(); MockLeanKitApi.Setup(x => x.GetBoard(It.IsAny<long>())).Returns(_testBoard); var serializer = new JsonSerializer<List<GitHubPulls.Pull>>(); var pull1 = new GitHubPulls.Pull() { Id = 1, Number = 1, State = "Open", Body = "New Pull 1", Title = "New Pull 1" }; var pull2 = new GitHubPulls.Pull() { Id = 2, Number = 2, State = "Open", Body = "New Pull 2", Title = "New Pull 2" }; var pull3 = new GitHubPulls.Pull() { Id = 3, Number = 3, State = "Open", Body = "New Pull 3", Title = "New Pull 3" }; var restResponse1 = new RestResponse() { Content = serializer.SerializeToString(new List<GitHubPulls.Pull>() {pull1}), StatusCode = HttpStatusCode.OK }; MockRestClient.Setup( x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("/1/pulls") && y.Method == Method.GET))) .Returns(restResponse1); MockLeanKitApi.Setup(x => x.GetCardByExternalId(1, It.IsAny<string>())).Returns((Card) null); MockLeanKitApi.Setup(x => x.AddCard(1, It.IsAny<Card>(), It.IsAny<string>())).Returns(_testCardAddResult1); MockRestClient.Setup( x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("/2/pulls") && y.Method == Method.GET))) .Returns(restResponse1); MockLeanKitApi.Setup(x => x.GetCardByExternalId(2, It.IsAny<string>())).Returns((Card) null); MockLeanKitApi.Setup(x => x.AddCard(2, It.IsAny<Card>(), It.IsAny<string>())).Returns(_testCardAddResult1); var restResponse3 = new RestResponse() { Content = serializer.SerializeToString(new List<GitHubPulls.Pull>() {pull1, pull2, pull3}), StatusCode = HttpStatusCode.OK }; MockRestClient.Setup( x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("/3/pulls") && y.Method == Method.GET))) .Returns(restResponse3); MockLeanKitApi.Setup(x => x.GetCardByExternalId(3, It.IsAny<string>())).Returns((Card) null); MockLeanKitApi.Setup(x => x.AddCard(3, It.IsAny<Card>(), It.IsAny<string>())).Returns(_testCardAddResult1); MockRestClient.Setup( x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("/4/pulls") && y.Method == Method.GET))) .Returns(restResponse1); MockLeanKitApi.Setup(x => x.GetCardByExternalId(4, It.IsAny<string>())) .Returns(new Card() {Id = 4, ExternalSystemName = "GitHub"}); MockLeanKitApi.Setup(x => x.AddCard(4, It.IsAny<Card>(), It.IsAny<string>())).Returns(_testCardAddResult1); MockRestClient.Setup( x => x.Execute(It.Is<RestRequest>(y => y.Resource.Contains("/5/pulls") && y.Method == Method.GET))) .Returns(restResponse1); MockLeanKitApi.Setup(x => x.GetCardByExternalId(5, It.IsAny<string>())) .Returns(new Card() {Id = 5, ExternalSystemName = "GitHubber"}); MockLeanKitApi.Setup(x => x.AddCard(5, It.IsAny<Card>(), It.IsAny<string>())).Returns(_testCardAddResult1); }
protected override void OnArrange() { base.OnArrange(); MockLeanKitApi.Setup(x => x.GetBoard(It.IsAny<long>())).Returns(_testBoard); var serializer = new JsonSerializer<Jira.IssuesResponse>(); var issue1 = new Jira.Issue() { Id = 1, Key = "one", Fields = new Jira.Fields() { Description = "Issue 1", Status = new Jira.Status() {Description = "Open", Id = "1", Name = "Open"}, Summary = "Issue 1" } }; var issue2 = new Jira.Issue() { Id = 2, Key = "two", Fields = new Jira.Fields() { Description = "Issue 2", Status = new Jira.Status() {Description = "Open", Id = "1", Name = "Open"}, Summary = "Issue 2" } }; var issue3 = new Jira.Issue() { Id = 3, Key = "three", Fields = new Jira.Fields() { Description = "Issue 3", Status = new Jira.Status() {Description = "Open", Id = "1", Name = "Open"}, Summary = "Issue 3" } }; var issueResponse1 = new Jira.IssuesResponse() { Issues = new List<Jira.Issue>() {issue1} }; var restResponse1 = new RestResponse() { Content = serializer.SerializeToString(issueResponse1), StatusCode = HttpStatusCode.OK }; MockRestClient.Setup( x => x.Execute( It.Is<RestRequest>( y => y.Resource.Contains("/search") && y.Method == Method.GET && y.Parameters.FirstOrDefault(z => z.Name == "jql" && z.Value.ToString().Contains("project=\"1\"")) != null))) .Returns(restResponse1); MockLeanKitApi.Setup(x => x.GetCardByExternalId(1, It.IsAny<string>())).Returns((Card) null); MockLeanKitApi.Setup(x => x.AddCard(1, It.IsAny<Card>(), It.IsAny<string>())).Returns(_testCardAddResult1); MockRestClient.Setup( x => x.Execute( It.Is<RestRequest>( y => y.Resource.Contains("/search") && y.Method == Method.GET && y.Parameters.FirstOrDefault(z => z.Name == "jql" && z.Value.ToString().Contains("project=\"2\"")) != null))) .Returns(restResponse1); MockLeanKitApi.Setup(x => x.GetCardByExternalId(2, It.IsAny<string>())).Returns((Card) null); MockLeanKitApi.Setup(x => x.AddCard(2, It.IsAny<Card>(), It.IsAny<string>())).Returns(_testCardAddResult1); var issueResponse3 = new Jira.IssuesResponse() { Issues = new List<Jira.Issue>() {issue1, issue2, issue3} }; var restResponse3 = new RestResponse() { Content = serializer.SerializeToString(issueResponse3), StatusCode = HttpStatusCode.OK }; MockRestClient.Setup( x => x.Execute( It.Is<RestRequest>( y => y.Resource.Contains("/search") && y.Method == Method.GET && y.Parameters.FirstOrDefault(z => z.Name == "jql" && z.Value.ToString().Contains("project=\"3\"")) != null))) .Returns(restResponse3); MockLeanKitApi.Setup(x => x.GetCardByExternalId(3, It.IsAny<string>())).Returns((Card) null); MockLeanKitApi.Setup(x => x.AddCard(3, It.IsAny<Card>(), It.IsAny<string>())).Returns(_testCardAddResult1); MockRestClient.Setup( x => x.Execute( It.Is<RestRequest>( y => y.Resource.Contains("/search") && y.Method == Method.GET && y.Parameters.FirstOrDefault(z => z.Name == "jql" && z.Value.ToString().Contains("project=\"4\"")) != null))) .Returns(restResponse1); MockLeanKitApi.Setup(x => x.GetCardByExternalId(4, It.IsAny<string>())) .Returns(new Card() {Id = 4, ExternalSystemName = "Jira"}); MockLeanKitApi.Setup(x => x.AddCard(4, It.IsAny<Card>(), It.IsAny<string>())).Returns(_testCardAddResult1); MockRestClient.Setup( x => x.Execute( It.Is<RestRequest>( y => y.Resource.Contains("/search") && y.Method == Method.GET && y.Parameters.FirstOrDefault(z => z.Name == "jql" && z.Value.ToString().Contains("project=\"5\"")) != null))) .Returns(restResponse1); MockLeanKitApi.Setup(x => x.GetCardByExternalId(5, It.IsAny<string>())) .Returns(new Card() {Id = 4, ExternalSystemName = "Jirabus"}); MockLeanKitApi.Setup(x => x.AddCard(5, It.IsAny<Card>(), It.IsAny<string>())).Returns(_testCardAddResult1); }