Beispiel #1
0
        public static CardType CalculateLeanKitCardType(BoardMapping project, string issueTypeName)
        {
            if (string.IsNullOrEmpty(issueTypeName))
            {
                return(project.ValidCardTypes.FirstOrDefault(x => x.IsDefault));
            }

            var mappedWorkType =
                project.Types.FirstOrDefault(x => x.Target.Equals(issueTypeName, StringComparison.InvariantCultureIgnoreCase));

            if (mappedWorkType != null)
            {
                var definedVal =
                    project.ValidCardTypes.FirstOrDefault(
                        x => x.Name.Equals(mappedWorkType.LeanKit, StringComparison.InvariantCultureIgnoreCase));
                if (definedVal != null)
                {
                    return(definedVal);
                }
            }
            var implicitVal =
                project.ValidCardTypes.FirstOrDefault(x => x.Name.Equals(issueTypeName, StringComparison.InvariantCultureIgnoreCase));

            return(implicitVal ?? project.ValidCardTypes.FirstOrDefault(x => x.IsDefault));
        }
        public static CardType CalculateLeanKitCardType(BoardMapping project, GitHubIssues.Issue issue)
        {
            // NOTE: GitHub does not use types for issues. It uses labels.
            // Default labels are: bug, duplicate, enhancement, invalid, question, wont fix
            // of those bug and enhancement are the ones that fit a type the best.
            // bug could be mapped to bug/issue in LeanKit and enhancement mapped to improvement/feature in LeanKit

            var defaultCardType = project.ValidCardTypes.FirstOrDefault(x => x.IsDefault);

            if (issue != null && issue.Labels != null && issue.Labels.Any())
            {
                foreach (var label in issue.Labels)
                {
                    var mappedWorkType = project.Types.FirstOrDefault(x => x.Target.ToLowerInvariant() == label.Name.ToLowerInvariant());
                    if (mappedWorkType != null)
                    {
                        var definedVal = project.ValidCardTypes.FirstOrDefault(x => x.Name.ToLowerInvariant() == mappedWorkType.LeanKit.ToLowerInvariant());
                        if (definedVal != null)
                        {
                            return(definedVal);
                        }
                    }
                    var implicitVal = project.ValidCardTypes.FirstOrDefault(x => x.Name.ToLowerInvariant() == label.Name.ToLowerInvariant());
                    if (implicitVal != null)
                    {
                        return(implicitVal);
                    }
                }
            }
            return(defaultCardType);
        }
Beispiel #3
0
 protected override void OnStartFixture()
 {
     _testBoard = Test <Board> .Item;
     foreach (var cardType in _testBoard.CardTypes)
     {
         cardType.IsDefault = false;
     }
     _testBoard.CardTypes.Add(new CardType()
     {
         Id = 999, Name = "Willy", IsDefault = false
     });
     _testBoard.CardTypes.Last().IsDefault = true;
     _mapping = Test <BoardMapping> .Item;
     _mapping.Identity.LeanKit = _testBoard.Id;
     _mapping.Types            = new List <WorkItemType>()
     {
         new WorkItemType()
         {
             LeanKit = "Willy", Target = "Roger"
         }
     };
     TestConfig          = Test <Configuration> .Item;
     TestConfig.Mappings = new List <BoardMapping> {
         _mapping
     };
 }
		public static CardType CalculateLeanKitCardType(BoardMapping project, GitHubIssues.Issue issue) 
		{
			// NOTE: GitHub does not use types for issues. It uses labels. 
			// Default labels are: bug, duplicate, enhancement, invalid, question, wont fix
			// of those bug and enhancement are the ones that fit a type the best. 
			// bug could be mapped to bug/issue in LeanKit and enhancement mapped to improvement/feature in LeanKit

			var defaultCardType = project.ValidCardTypes.FirstOrDefault(x => x.IsDefault);

			if (issue != null && issue.Labels != null && issue.Labels.Any()) {
				foreach (var label in issue.Labels) {
					var mappedWorkType = project.Types.FirstOrDefault(x => x.Target.ToLowerInvariant() == label.Name.ToLowerInvariant());
					if (mappedWorkType != null) {
						var definedVal = project.ValidCardTypes.FirstOrDefault(x => x.Name.ToLowerInvariant() == mappedWorkType.LeanKit.ToLowerInvariant());
						if (definedVal != null) {
							return definedVal;
						}
					}
					var implicitVal = project.ValidCardTypes.FirstOrDefault(x => x.Name.ToLowerInvariant() == label.Name.ToLowerInvariant());
					if (implicitVal != null) {
						return implicitVal;
					}
				}
			}
			return defaultCardType;
		}
        protected override void OnStartFixture()
        {
            _testBoard = Test <Board> .Item;
            int x = 0;

            foreach (var lane in _testBoard.Lanes)
            {
                lane.Active    = true;
                lane.ClassType = LaneClassType.Active;
                lane.Index     = x++;
            }

            // assign an arbitrary archive lane as the top level archive
            var lastArchiveItem = _testBoard.Archive.Last();

            lastArchiveItem.ParentLaneId     = 0;
            _testBoard.ArchiveTopLevelLaneId = lastArchiveItem.Id;

            activeLaneCount           = _testBoard.Lanes.Count;
            _mapping                  = Test <BoardMapping> .Item;
            _mapping.Identity.LeanKit = _testBoard.Id;
            TestConfig                = Test <Configuration> .Item;
            TestConfig.Mappings       = new List <BoardMapping> {
                _mapping
            };
        }
Beispiel #6
0
        public static CardType CalculateLeanKitCardType(BoardMapping project, string issueTypeName)
        {
            var boardId = project.Identity.LeanKit;

            if (!string.IsNullOrEmpty(issueTypeName))
            {
                var mappedWorkType = project.Types.FirstOrDefault(x => x.Target.ToLowerInvariant() == issueTypeName.ToLowerInvariant());
                if (mappedWorkType != null)
                {
                    var definedVal =
                        project.ValidCardTypes.FirstOrDefault(x => x.Name.ToLowerInvariant() == mappedWorkType.LeanKit.ToLowerInvariant());
                    if (definedVal != null)
                    {
                        return(definedVal);
                    }
                }
                var implicitVal =
                    project.ValidCardTypes.FirstOrDefault(x => x.Name.ToLowerInvariant() == issueTypeName.ToLowerInvariant());
                if (implicitVal != null)
                {
                    return(implicitVal);
                }
            }

            return(project.ValidCardTypes.FirstOrDefault(x => x.IsDefault));
        }
        protected override void Synchronize(BoardMapping project)
        {
            Log.Debug("Polling GitHub for Pull Requests");

            var queryAsOfDate = QueryDate.AddMilliseconds(Configuration.PollingFrequency * -1.5);

            //https://api.github.com/repos/{0}/{1}/pulls?state=Open
            var request = new RestRequest(string.Format("repos/{0}/{1}/pulls", Configuration.Target.Host, project.Identity.Target), Method.GET);

            request.AddParameter("state", project.QueryStates[0]);

            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 Pull Requests from GitHub, Error: {0}. Check your board/repo mapping configuration.", errorMessage.Message));
                return;
            }

            var pulls = new JsonSerializer <List <Pull> >().DeserializeFromString(resp.Content);

            Log.Info("\nQueried [{0}] at {1} for changes after {2}", project.Identity.Target, QueryDate, queryAsOfDate.ToString("o"));

            if (pulls != null && pulls.Any() && pulls[0].Id > 0)
            {
                foreach (var pull in pulls)
                {
                    if (pull.Id > 0)
                    {
                        Log.Info("Pull Requests [{0}]: {1}, {2}, {3}", pull.Number, pull.Title, pull.User.Login, pull.State);

                        // does this workitem have a corresponding card?
                        var card = LeanKit.GetCardByExternalId(project.Identity.LeanKit, pull.Id + "|" + pull.Number.ToString());

                        if (card == null || !card.ExternalSystemName.Equals(ServiceName, StringComparison.OrdinalIgnoreCase))
                        {
                            Log.Debug("Create new card for Pull Request [{0}]", pull.Number);
                            CreateCardFromItem(project, pull);
                        }
                        else
                        {
                            Log.Debug("Previously created a card for Pull Request [{0}]", pull.Number);
                            if (project.UpdateCards)
                            {
                                PullUpdated(pull, card, project);
                            }
                            else
                            {
                                Log.Info("Skipped card update because 'UpdateCards' is disabled.");
                            }
                        }
                    }
                }
                Log.Info("{0} item(s) queried.\n", pulls.Count);
            }
        }
Beispiel #8
0
    void Start()
    {
        br      = GameObject.FindObjectOfType(typeof(BoardRules)) as BoardRules;
        ps      = GameObject.FindObjectOfType(typeof(PlayerScript)) as PlayerScript;
        bm      = GameObject.FindObjectOfType(typeof(BoardMapping)) as BoardMapping;
        cor     = "Black";
        cor_adv = "White";

        //receber como parametro cor escolhida pelo jogador
    }
 protected override void OnStartFixture()
 {
     _testBoard = Test <Board> .Item;
     _mapping   = Test <BoardMapping> .Item;
     _mapping.Identity.LeanKit = _testBoard.Id;
     TestConfig          = Test <Configuration> .Item;
     TestConfig.Mappings = new List <BoardMapping> {
         _mapping
     };
 }
 protected override void OnStartFixture()
 {
     _testBoard = Test <Board> .Item;
     _testBoard.ArchiveTopLevelLaneId   = null;
     _testBoard.Archive[0].ParentLaneId = 0;
     _mapping = Test <BoardMapping> .Item;
     _mapping.Identity.LeanKit = _testBoard.Id;
     TestConfig          = Test <Configuration> .Item;
     TestConfig.Mappings = new List <BoardMapping> {
         _mapping
     };
 }
		public static CardType CalculateLeanKitCardType(BoardMapping project, string issueTypeName) 
		{
			if (string.IsNullOrEmpty(issueTypeName)) return project.ValidCardTypes.FirstOrDefault(x => x.IsDefault);

			var mappedWorkType = project.Types.FirstOrDefault(x => x.Target.Equals(issueTypeName, StringComparison.InvariantCultureIgnoreCase));
			if (mappedWorkType != null) 
			{
				var definedVal = project.ValidCardTypes.FirstOrDefault(x => x.Name.Equals(mappedWorkType.LeanKit, StringComparison.InvariantCultureIgnoreCase));
				if (definedVal != null)  return definedVal;
			}
			var implicitVal = project.ValidCardTypes.FirstOrDefault(x => x.Name.Equals(issueTypeName, StringComparison.InvariantCultureIgnoreCase));
			return implicitVal ?? project.ValidCardTypes.FirstOrDefault(x => x.IsDefault);
		}
Beispiel #12
0
    // Use this for initialization
    void Start()
    {
        gm = GameObject.FindObjectOfType(typeof(GameManager)) as GameManager;
        bm = GameObject.FindObjectOfType(typeof(BoardMapping)) as BoardMapping;
        br = GameObject.FindObjectOfType(typeof(BoardRules)) as BoardRules;

        piece       = null;
        pieceToMove = null;
        tile        = null;

        ctrlMove       = false;
        podeJogar      = false;
        podeMudarTurno = true;
    }
Beispiel #13
0
 protected override void OnStartFixture()
 {
     _testBoard = Test <Board> .Item;
     foreach (var cardType in _testBoard.CardTypes)
     {
         cardType.IsDefault = false;
     }
     _testBoard.CardTypes.Last().IsDefault = true;
     _mapping = Test <BoardMapping> .Item;
     _mapping.Identity.LeanKit = _testBoard.Id;
     TestConfig          = Test <Configuration> .Item;
     TestConfig.Mappings = new List <BoardMapping> {
         _mapping
     };
 }
        protected override void OnStartFixture()
        {
            _testBoard = Test <Board> .Item;
            _testBoard.ArchiveTopLevelLaneId = null;
            var archiveLane = _testBoard.AllLanes().FirstOrDefault(x => x.ClassType == LaneClassType.Archive);

            archiveLane.ParentLaneId = 0;
            _mapping = Test <BoardMapping> .Item;
            _mapping.Identity.LeanKit = _testBoard.Id;
            _mapping.ArchiveLaneId    = 0;
            TestConfig          = Test <Configuration> .Item;
            TestConfig.Mappings = new List <BoardMapping> {
                _mapping
            };
        }
        private void PullUpdated(Pull pull, Card card, BoardMapping boardMapping)
        {
            Log.Info("Pull [{0}] updated, comparing to corresponding card...", pull.Id);

            long boardId = boardMapping.Identity.LeanKit;

            // sync and save those items that are different (of title, description, priority)
            bool saveCard = false;

            if (pull.Title != card.Title)
            {
                card.Title = pull.Title;
                saveCard   = true;
            }

            if (pull.Body.SanitizeCardDescription() != card.Description)
            {
                card.Description = pull.Body.SanitizeCardDescription();
                saveCard         = true;
            }

            var priority = pull.LeanKitPriority();

            if (priority != card.Priority)
            {
                card.Priority = priority;
                saveCard      = true;
            }

            if ((card.Tags == null || !card.Tags.Contains(ServiceName)) && boardMapping.TagCardsWithTargetSystemName)
            {
                if (string.IsNullOrEmpty(card.Tags))
                {
                    card.Tags = ServiceName;
                }
                else
                {
                    card.Tags += "," + ServiceName;
                }
                saveCard = true;
            }

            if (saveCard)
            {
                Log.Info("Updating card [{0}]", card.Id);
                LeanKit.UpdateCard(boardId, card);
            }
        }
 protected override void OnStartFixture()
 {
     _testBoard                 = Test <Board> .Item;
     _mapping                   = Test <BoardMapping> .Item;
     _testCardAddResult1        = Test <CardAddResult> .Item;
     _testCardAddResult1.CardId = 1;
     _mapping.Identity.LeanKit  = _testBoard.Id;
     _mapping.LaneToStatesMap.Add(1, new List <string> {
         "open"
     });
     TestConfig = Test <Configuration> .Item;
     TestConfig.PollingFrequency = 5000;
     TestConfig.Mappings         = new List <BoardMapping> {
         _mapping
     };
 }
 protected override void OnStartFixture()
 {
     _testBoard = Test <Board> .Item;
     _mapping   = Test <BoardMapping> .Item;
     _mapping.Identity.LeanKit = _testBoard.Id;
     _mapping.LaneToStatesMap.Add(1, new List <string> {
         "open"
     });
     _mapping.LaneToStatesMap.Add(2, new List <string> {
         "accepted>resolved>closed"
     });
     TestConfig          = Test <Configuration> .Item;
     TestConfig.Mappings = new List <BoardMapping> {
         _mapping
     };
 }
 protected override void OnStartFixture()
 {
     _testBoard   = Test <Board> .Item;
     allLaneCount = _testBoard.AllLanes().Count(x => x.ClassType != LaneClassType.Archive);
     // insure at least 1 active lane for testing
     if (allLaneCount == 0)
     {
         _testBoard.Lanes[0].ClassType = LaneClassType.Active;
         allLaneCount = 1;
     }
     _mapping = Test <BoardMapping> .Item;
     _mapping.Identity.LeanKit = _testBoard.Id;
     TestConfig          = Test <Configuration> .Item;
     TestConfig.Mappings = new List <BoardMapping> {
         _mapping
     };
 }
		public static CardType CalculateLeanKitCardType(BoardMapping project, string issueTypeName) 
		{
			var boardId = project.Identity.LeanKit;

			if (!string.IsNullOrEmpty(issueTypeName)) {
				var mappedWorkType = project.Types.FirstOrDefault(x => x.Target.ToLowerInvariant() == issueTypeName.ToLowerInvariant());
				if (mappedWorkType != null) {
					var definedVal =
						project.ValidCardTypes.FirstOrDefault(x => x.Name.ToLowerInvariant() == mappedWorkType.LeanKit.ToLowerInvariant());
					if (definedVal != null) {
						return definedVal;
					}
				}
				var implicitVal =
					project.ValidCardTypes.FirstOrDefault(x => x.Name.ToLowerInvariant() == issueTypeName.ToLowerInvariant());
				if (implicitVal != null) {
					return implicitVal;
				}
			}

			return project.ValidCardTypes.FirstOrDefault(x => x.IsDefault);
		}
Beispiel #20
0
        protected override void OnStartFixture()
        {
            _testBoard = Test <Board> .Item;
            int ctr = 0;

            foreach (var boardUser in _testBoard.BoardUsers)
            {
                if (ctr == 0)
                {
                    boardUser.UserName     = "******";
                    boardUser.FullName     = "Johnny Cash";
                    boardUser.EmailAddress = "*****@*****.**";
                    boardUser.Id           = 101;
                }
                ctr++;
            }
            _mapping = Test <BoardMapping> .Item;
            _mapping.Identity.LeanKit = _testBoard.Id;
            TestConfig          = Test <Configuration> .Item;
            TestConfig.Mappings = new List <BoardMapping> {
                _mapping
            };
        }
Beispiel #21
0
        private string GetJiraIssueType(BoardMapping boardMapping, long cardTypeId)
        {
            const string defaultIssueType = "Bug";

            if (cardTypeId <= 0 ||
                boardMapping == null ||
                boardMapping.Types == null ||
                boardMapping.ValidCardTypes == null ||
                !boardMapping.ValidCardTypes.Any() ||
                !boardMapping.Types.Any())
            {
                return(defaultIssueType);
            }

            var lkType = boardMapping.ValidCardTypes.FirstOrDefault(x => x.Id == cardTypeId);

            if (lkType == null)
            {
                return(defaultIssueType);
            }
            var mappedType = boardMapping.Types.FirstOrDefault(x => x != null && !string.IsNullOrEmpty(x.LeanKit) && String.Equals(x.LeanKit, lkType.Name, StringComparison.OrdinalIgnoreCase));

            return(mappedType != null ? mappedType.Target : "Bug");
        }
        protected override void OnStartFixture()
        {
            _testBoard = Test <Board> .Item;
            int x = 0;

            foreach (var lane in _testBoard.Lanes)
            {
                lane.Active       = true;
                lane.ClassType    = LaneClassType.Active;
                lane.ChildLaneIds = null;
                lane.ParentLaneId = 0;
                lane.Index        = x++;
            }

            allLaneCount = _testBoard.AllLanes().Count(y => y.ClassType != LaneClassType.Archive);
            _mapping     = Test <BoardMapping> .Item;
            _mapping.Identity.LeanKit        = _testBoard.Id;
            _mapping.ArchiveLaneId           = 0;
            _testBoard.ArchiveTopLevelLaneId = null;
            TestConfig          = Test <Configuration> .Item;
            TestConfig.Mappings = new List <BoardMapping> {
                _mapping
            };
        }
 public static CardType LeanKitCardType(this Jira.Issue issue, BoardMapping project)
 {
     return CalculateLeanKitCardType(project, issue.Fields.IssueType.Name);
 }
Beispiel #24
0
 protected override void UpdateStateOfExternalItem(Card card, List <string> states, BoardMapping boardMapping)
 {
     UpdateStateOfExternalItem(card, states, boardMapping, false);
 }
Beispiel #25
0
        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 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);
			}
		}
Beispiel #27
0
        private void IssueUpdated(Issue issue, Card card, BoardMapping boardMapping)
        {
            Log.Info("Issue [{0}] updated, comparing to corresponding card...", issue.Id);

            long boardId = boardMapping.Identity.LeanKit;

            // sync and save those items that are different (of title, description, priority)
            bool saveCard = false;

            if (issue.Title != card.Title)
            {
                card.Title = issue.Title;
                saveCard   = true;
            }

            if (issue.Body.SanitizeCardDescription() != card.Description)
            {
                card.Description = issue.Body.SanitizeCardDescription();
                saveCard         = true;
            }

            var priority = issue.LeanKitPriority();

            if (priority != card.Priority)
            {
                card.Priority = priority;
                saveCard      = true;
            }

            if (issue.Labels != null && issue.Labels.Count > 0)
            {
                var tags = string.Join(",", issue.Labels.Select(x => x.Name));
                if (card.Tags != tags)
                {
                    card.Tags = tags;
                    saveCard  = true;
                }
            }
            else if (!string.IsNullOrEmpty(card.Tags))
            {
                card.Tags = "";
                saveCard  = true;
            }

            if (issue.Milestone != null && issue.Milestone.Due_On != null)
            {
                if (CurrentUser != null)
                {
                    var dateFormat    = CurrentUser.DateFormat ?? "MM/dd/yyyy";
                    var dueDateString = issue.Milestone.Due_On.Value.ToString(dateFormat);
                    if (card.DueDate != dueDateString)
                    {
                        card.DueDate = dueDateString;
                        saveCard     = true;
                    }
                }
            }
            else if (!string.IsNullOrEmpty(card.DueDate))
            {
                card.DueDate = "";
                saveCard     = true;
            }

            if ((card.Tags == null || !card.Tags.Contains(ServiceName)) && boardMapping.TagCardsWithTargetSystemName)
            {
                if (string.IsNullOrEmpty(card.Tags))
                {
                    card.Tags = ServiceName;
                }
                else
                {
                    card.Tags += "," + ServiceName;
                }
                saveCard = true;
            }

            var lanes = boardMapping.LanesFromState(issue.State);

            if (lanes.Count > 0 && lanes.All(x => x != card.LaneId))
            {
                card.LaneId = lanes.First();
                saveCard    = true;
            }

            if (saveCard)
            {
                Log.Info("Updating card [{0}]", card.Id);
                LeanKit.UpdateCard(boardId, card);
            }
        }
		private Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemType GetTfsWorkItemType(BoardMapping boardMapping, WorkItemTypeCollection workItemTypes, long cardTypeId)
		{			
			if (boardMapping != null && 
				boardMapping.Types != null && 
				boardMapping.ValidCardTypes != null && 
				boardMapping.Types.Any() && 
				boardMapping.ValidCardTypes.Any() )
			{				
				var lkType = boardMapping.ValidCardTypes.FirstOrDefault(x => x.Id == cardTypeId);
				if (lkType != null)
				{
					// first check for mapped type
					var mappedType = boardMapping.Types.FirstOrDefault(x => x.LeanKit.ToLowerInvariant() == lkType.Name.ToLowerInvariant());
					if (mappedType != null) 
					{
						if (workItemTypes.Contains(mappedType.Target))
							return workItemTypes[mappedType.Target];
					}
					// now check for implicit type
					if (workItemTypes.Contains(lkType.Name))
						return workItemTypes[lkType.Name];
				}

			}
			// else just return the first type from list of types from TFS
			return workItemTypes[0];
		}
Beispiel #29
0
 protected abstract void CardUpdated(Card card, List <string> updatedItems, BoardMapping boardMapping);
			public void TestCardUpdated(Card card, List<string> updatedItems, BoardMapping boardMapping)
			{
				base.CardUpdated(card, updatedItems, boardMapping);
			}
	    private void CheckForMissedCardMoves(BoardMapping mapping)
	    {
			if (!mapping.UpdateTargetItems)
			{
				Log.Info("Skipped check for missed card moves because 'UpdateTargetItems' is disabled.");
				return;
			}

			// if we have local storage, we have saved board versions and we have one for this board			
			var boardId = mapping.Identity.LeanKit;
		    if (AppSettings == null || AppSettings.BoardVersions == null || !AppSettings.BoardVersions.Any() ||
		        !AppSettings.BoardVersions.ContainsKey(boardId)) return;

		    var version = AppSettings.BoardVersions[boardId];
		    Log.Debug(string.Format("Checking for any cards moved to mapped lanes on board [{0}] since service last ran, version [{1}].", boardId, version));
		    try
		    {
			    var events = LeanKit.GetBoardHistorySince(boardId, version);
			    var board = LeanKit.GetBoard(boardId);
			    if (board == null || events == null) return;

			    foreach (var ev in events)
			    {
				    // check for created cards
				    if (ev.EventType == "CardCreation")
				    {
					    var card = LeanKit.GetCard(board.Id, ev.CardId);
					    if (card != null && string.IsNullOrEmpty(card.ExternalCardID))
					    {
						    try
						    {
							    CreateNewItem(card.ToCard(), mapping);
						    }
						    catch (Exception e)
						    {
							    Log.Error("Exception for CreateNewItem: " + e.Message);
						    }
					    }
				    }
				    // only look for moved cards
				    else if (ev.ToLaneId != 0)
				    {
					    var lane = board.GetLaneById(ev.ToLaneId);
					    if (lane != null)
					    {
						    if (lane.Id.HasValue && mapping.LaneToStatesMap.Any() && mapping.LaneToStatesMap.ContainsKey(lane.Id.Value))
						    {
							    if (mapping.LaneToStatesMap[lane.Id.Value] != null && mapping.LaneToStatesMap[lane.Id.Value].Count > 0)
							    {
								    // board.GetCard() only seems to get cards in active lanes
								    // using LeanKitApi.GetCard() instead because it will get 
								    // cards in archive lanes
								    var card = LeanKit.GetCard(board.Id, ev.CardId);
								    if (card != null && !string.IsNullOrEmpty(card.ExternalCardID))
								    {
									    try {
										    UpdateStateOfExternalItem(card.ToCard(), mapping.LaneToStatesMap[lane.Id.Value], mapping);
									    } catch (Exception e) {
										    Log.Error("Exception for UpdateStateOfExternalItem: " + e.Message);
									    }
								    }
							    }
						    }
					    }
				    }					    
			    }
			    UpdateBoardVersion(board.Id, board.Version);
		    }
		    catch (Exception ex)
		    {
			    Log.Error(string.Format("An error occured: {0} - {1} - {2}", ex.GetType(), ex.Message, ex.StackTrace));
		    }
	    }
		protected override void OnStartFixture()
		{
			_testBoard = Test<Board>.Item;
			foreach (var cardType in _testBoard.CardTypes)
				cardType.IsDefault = false;
			_testBoard.CardTypes.Last().IsDefault = true;
			_mapping = Test<BoardMapping>.Item;
			_mapping.Identity.LeanKit = _testBoard.Id;
			TestConfig = Test<Configuration>.Item;
			TestConfig.Mappings = new List<BoardMapping> {_mapping};
		}
		protected override void OnStartFixture()
		{
			_testBoard = Test<Board>.Item;
			int ctr = 0;
			foreach (var boardUser in _testBoard.BoardUsers)
			{
				if (ctr == 0)
				{
					boardUser.UserName = "******";
					boardUser.FullName = "Johnny Cash";
					boardUser.EmailAddress = "*****@*****.**";
					boardUser.Id = 101;
				}
				ctr++;
			}
			_mapping = Test<BoardMapping>.Item;
			_mapping.Identity.LeanKit = _testBoard.Id;
			TestConfig = Test<Configuration>.Item;
			TestConfig.Mappings = new List<BoardMapping> {_mapping};
		}
Beispiel #34
0
        private void LoadBoardValues(BoardMapping boardMapping)
        {
            Board board = null;

            try
            {
                board = LeanKit.GetBoard(boardMapping.Identity.LeanKit);
            }
            catch (LeanKitAPIException ex)
            {
                Log.Error(ex, string.Format("Error getting Board: {0}", boardMapping.Identity.LeanKit));
            }

            if (board == null)
            {
                return;
            }

            if (board.CardTypes != null && board.CardTypes.Any())
            {
                boardMapping.ValidCardTypes = board.CardTypes;

                // check to make sure we have a default card type
                var defaultCard = boardMapping.ValidCardTypes.FirstOrDefault(x => x.IsDefault);
                if (defaultCard == null)
                {
                    // if we do not have a default card type then check
                    // to see if there is a Task card type and make that the default
                    var taskCardType = boardMapping.ValidCardTypes.FirstOrDefault(x => x.Name.ToLowerInvariant() == "task");
                    if (taskCardType != null)
                    {
                        boardMapping.ValidCardTypes.FirstOrDefault(x => x.Name.ToLowerInvariant() == "task").IsDefault = true;
                    }
                    else
                    {
                        // otherwise just set the first card type to be the default
                        boardMapping.ValidCardTypes.FirstOrDefault().IsDefault = true;
                    }
                }
            }

            if (board.ArchiveTopLevelLaneId.HasValue)
            {
                boardMapping.ArchiveLaneId = board.ArchiveTopLevelLaneId.Value;
            }
            else
            {
                var archive = board.Archive.FirstOrDefault(x => x.ParentLaneId == 0);
                if (archive != null && archive.Id.HasValue)
                {
                    boardMapping.ArchiveLaneId = archive.Id.Value;
                }

                if (boardMapping.ArchiveLaneId == 0)
                {
                    var allLanes = board.AllLanes();
                    archive = allLanes.FirstOrDefault(x => x.ClassType == LaneClassType.Archive && x.ParentLaneId == 0);
                    if (archive != null && archive.Id.HasValue)
                    {
                        boardMapping.ArchiveLaneId = archive.Id.Value;
                    }
                }
            }

            if (board.Lanes != null && board.Lanes.Any())
            {
                var validLanes = board.AllLanes().Where(x => x.ClassType != LaneClassType.Archive).OrderBy(x => x.Index).ToList();
                var maxIndex   = validLanes.Max(x => x.Index);

                // only use active lanes for the purpose of selecting the default drop lane
                var activeLanes       = board.Lanes.Where(x => x.ClassType == LaneClassType.Active).OrderBy(x => x.Index).ToList();
                var defaultDropLaneId = GetDefaultDropLane(activeLanes);

                boardMapping.ValidLanes = validLanes
                                          .Select(x => new Lane
                {
                    Id           = x.Id.Value,
                    Name         = x.Title,
                    IsFirst      = x.Id == defaultDropLaneId,
                    ChildLaneIds = (x.ChildLaneIds != null && x.ChildLaneIds.Any()) ? x.ChildLaneIds : null,
                    IsLast       = (boardMapping.ArchiveLaneId > 0) ? x.Id == boardMapping.ArchiveLaneId : x.Index == maxIndex
                })
                                          .ToList();

                if (boardMapping.ArchiveLaneId > 0)
                {
                    var archiveLane = board.GetLaneById(boardMapping.ArchiveLaneId);
                    if (archiveLane != null)
                    {
                        boardMapping.ValidLanes.Add(new Lane
                        {
                            Id      = boardMapping.ArchiveLaneId,
                            Name    = archiveLane.Title,
                            IsFirst = false,
                            IsLast  = true
                        });
                    }
                }
            }

            if (boardMapping.Types == null)
            {
                boardMapping.Types = new List <WorkItemType>();
            }

            // values in LaneToStatesMap are assumed to valid, as they were configured using valid values.
            if (boardMapping.LaneToStatesMap == null)
            {
                Log.Fatal("An unexpected error occurred -- there is no valid lane-to-states mapping.");
            }
        }
Beispiel #35
0
 protected abstract void Synchronize(BoardMapping boardMapping);
Beispiel #36
0
 protected abstract void CreateNewItem(Card card, BoardMapping boardMapping);
		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 abstract void CardUpdated(Card card, List<string> updatedItems, BoardMapping boardMapping);
	    protected override void CardUpdated(Card card, List<string> updatedItems, BoardMapping boardMapping)
	    {
		    if (!card.ExternalSystemName.Equals(ServiceName, StringComparison.OrdinalIgnoreCase))
			    return;

		    if (string.IsNullOrEmpty(card.ExternalCardID))
			    return;

		    Log.Info("Card [{0}] updated.", card.Id);

		    int workItemId;
		    try
		    {
			    workItemId = Convert.ToInt32(card.ExternalCardID);
		    }
		    catch (Exception)
		    {
			    Log.Debug("Ignoring card [{0}] with missing external id value.", card.Id);
			    return;
		    }

		    Log.Debug("Attempting to load Work Item [{0}]", workItemId);
		    WorkItem workItem;
		    try
		    {
			    workItem = _projectCollectionWorkItemStore.GetWorkItem(workItemId);
		    }
		    catch (Exception ex)
		    {
			    Log.Error(ex, string.Format("Could not load Work Item [{0}]", workItemId));
			    return;
		    }

		    if (workItem == null)
		    {
			    Log.Debug("Failed to find work item matching [{0}].", workItemId);
			    return;
		    }

		    if (updatedItems.Contains("Title") && workItem.Title != card.Title)
			    workItem.Title = card.Title;


		    if (updatedItems.Contains("Description"))
		    {
			    var description = workItem.LeanKitDescription(GetTfsVersion());
			    if (description != card.Description)
			    {
				    if (workItem.UseReproSteps())
					    workItem.Fields["Repro Steps"].Value = card.Description;
				    else
					    workItem.Description = card.Description;
			    }
		    }

		    if (updatedItems.Contains("Priority"))
		    {
			    var currentWorkItemPriority = workItem.LeanKitPriority();
			    if (currentWorkItemPriority != card.Priority)
				    SetWorkItemPriority(workItem, card.Priority);
		    }

		    if (updatedItems.Contains("DueDate"))
		    {
			    SetDueDate(workItem, card.DueDate);
		    }

		    if (workItem.IsDirty)
		    {
			    Log.Info("Updating corresponding work item [{0}]", workItem.Id);
			    workItem.Save();
		    }

		    // unsupported properties; append changes to history

		    if (updatedItems.Contains("Size"))
		    {
			    workItem.History += "Card size changed to " + card.Size + "\r";
			    workItem.Save();
		    }

		    if (updatedItems.Contains("Blocked"))
		    {
			    if (card.IsBlocked)
				    workItem.History += "Card is blocked: " + card.BlockReason + "\r";
			    else
				    workItem.History += "Card is no longer blocked: " + card.BlockReason + "\r";
			    workItem.Save();
		    }

		    if (updatedItems.Contains("Tags"))
		    {
			    workItem.History += "Tags in LeanKit changed to " + card.Tags + "\r";
			    workItem.Save();
		    }

	    }
	    protected abstract void Synchronize(BoardMapping boardMapping);
		protected override void CreateNewItem(Card card, BoardMapping boardMapping) 
		{
			var project = _projectCollectionWorkItemStore.Projects[boardMapping.Identity.TargetName];
			var tfsWorkItemType = GetTfsWorkItemType(boardMapping, project.WorkItemTypes, card.TypeId);
			var workItemType = project.WorkItemTypes[tfsWorkItemType.Name];

			// Note: using the default state
			var workItem = new WorkItem(workItemType)
				{
					Title = card.Title,
					Description = card.Description,					
				};

			SetWorkItemPriority(workItem, card.Priority);
			
			if (!string.IsNullOrEmpty(card.DueDate))
			{
				if (workItem.Fields.Contains("Due Date"))
					workItem.Fields["Due Date"].Value = card.DueDate;
			}

			if (card.AssignedUserIds != null && card.AssignedUserIds.Any())
			{
				SetAssignedUser(workItem, boardMapping.Identity.LeanKit, card.AssignedUserIds[0]);
			}

			if (!string.IsNullOrEmpty(boardMapping.IterationPath)) {
				workItem.Fields["System.IterationPath"].Value = boardMapping.IterationPath;
			}

			try 
			{
				Log.Debug("Attempting to create Work Item from Card [{0}]", card.Id);

				workItem.Save();

				Log.Debug("Created Work Item [{0}] from Card [{1}]", workItem.Id, card.Id);

				card.ExternalCardID = workItem.Id.ToString(CultureInfo.InvariantCulture);
				card.ExternalSystemName = ServiceName;

				if (_projectHyperlinkService != null) 
				{
					card.ExternalSystemUrl = _projectHyperlinkService.GetWorkItemEditorUrl(workItem.Id).ToString();
				}	

				// 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 (ValidationException ex) 
			{
				Log.Error("Unable to create WorkItem from Card [{0}]. ValidationException: {1}", card.Id, ex.Message);
			} 
			catch (Exception ex) 
			{
				Log.Error("Unable to create WorkItem from Card [{0}], Exception: {1}", card.Id, ex.Message);
			}
		}
		protected override void OnStartFixture() 
		{
			_testBoard = Test<Board>.Item;
			foreach (var cardType in _testBoard.CardTypes)
				cardType.IsDefault = false;
			_testBoard.CardTypes.Add(new CardType() { Id = 999, Name = "Willy", IsDefault = false });
			_testBoard.CardTypes.Last().IsDefault = true;
			_mapping = Test<BoardMapping>.Item;
			_mapping.Identity.LeanKit = _testBoard.Id;
			_mapping.Types = new List<WorkItemType>() { new WorkItemType() { LeanKit = "Willy", Target = "Roger"}};
			TestConfig = Test<Configuration>.Item;
			TestConfig.Mappings = new List<BoardMapping> { _mapping };
		}
		protected abstract void UpdateStateOfExternalItem(Card card, List<string> states, BoardMapping boardMapping);
		protected override void OnStartFixture()
		{
			_testBoard = Test<Board>.Item;
			_mapping = Test<BoardMapping>.Item;
			_mapping.Identity.LeanKit = _testBoard.Id;
			_mapping.LaneToStatesMap.Add(1, new List<string> {"open"});
			_mapping.LaneToStatesMap.Add(2, new List<string> {"closed"});
			TestConfig = Test<Configuration>.Item;
			TestConfig.Mappings = new List<BoardMapping> {_mapping};
		}
	    protected abstract void CreateNewItem(Card card, BoardMapping boardMapping);
			public void Syncronize(BoardMapping boardConfig)
			{
				base.Synchronize(boardConfig);
			}
        private void LoadBoardValues(BoardMapping boardMapping)
        {
            Board board = null;
            try
            {
                board = LeanKit.GetBoard(boardMapping.Identity.LeanKit);
            }
            catch (LeanKitAPIException ex)
            {
                Log.Error(ex, string.Format("Error getting Board: {0}",  boardMapping.Identity.LeanKit));
            }
	        
			if (board == null) return;
	        
			if (board.CardTypes != null && board.CardTypes.Any())
	        {
		        boardMapping.ValidCardTypes = board.CardTypes;
                    
		        // check to make sure we have a default card type
		        var defaultCard = boardMapping.ValidCardTypes.FirstOrDefault(x => x.IsDefault);
		        if (defaultCard == null)
		        {
			        // if we do not have a default card type then check 
			        // to see if there is a Task card type and make that the default
			        var taskCardType = boardMapping.ValidCardTypes.FirstOrDefault(x => x.Name.ToLowerInvariant() == "task");
			        if (taskCardType != null)
			        {
				        boardMapping.ValidCardTypes.FirstOrDefault(x => x.Name.ToLowerInvariant() == "task").IsDefault = true;
			        }
			        else
			        {
				        // otherwise just set the first card type to be the default
				        boardMapping.ValidCardTypes.FirstOrDefault().IsDefault = true;
			        }
		        }
	        }

	        if (board.ArchiveTopLevelLaneId.HasValue)
	        {
		        boardMapping.ArchiveLaneId = board.ArchiveTopLevelLaneId.Value;
	        }
	        else
	        {
		        var archive = board.Archive.FirstOrDefault(x => x.ParentLaneId == 0);
		        if (archive != null && archive.Id.HasValue)
		        {
			        boardMapping.ArchiveLaneId = archive.Id.Value;
		        }

		        if (boardMapping.ArchiveLaneId == 0)
		        {
			        var allLanes = board.AllLanes();
			        archive = allLanes.FirstOrDefault(x => x.ClassType == LaneClassType.Archive && x.ParentLaneId == 0);
			        if (archive != null && archive.Id.HasValue)
			        {
				        boardMapping.ArchiveLaneId = archive.Id.Value;
			        }
		        }
	        }

	        if (board.Lanes != null && board.Lanes.Any())
	        {
		        var validLanes = board.AllLanes().Where(x => x.ClassType != LaneClassType.Archive).OrderBy(x => x.Index).ToList();
		        var maxIndex = validLanes.Max(x => x.Index);

		        // only use active lanes for the purpose of selecting the default drop lane
		        var activeLanes = board.Lanes.Where(x => x.ClassType == LaneClassType.Active).OrderBy(x => x.Index).ToList();
		        var defaultDropLaneId = GetDefaultDropLane(activeLanes);

		        boardMapping.ValidLanes = validLanes
			        .Select(x => new Lane
			        {
				        Id = x.Id.Value, 
				        Name = x.Title, 
				        IsFirst = x.Id == defaultDropLaneId, 
				        ChildLaneIds = (x.ChildLaneIds != null && x.ChildLaneIds.Any()) ? x.ChildLaneIds : null,
				        IsLast = (boardMapping.ArchiveLaneId > 0) ? x.Id == boardMapping.ArchiveLaneId : x.Index == maxIndex
			        })
			        .ToList();

		        if (boardMapping.ArchiveLaneId > 0)
		        {
			        var archiveLane = board.GetLaneById(boardMapping.ArchiveLaneId);
			        if (archiveLane != null)
			        {
				        boardMapping.ValidLanes.Add(new Lane
				        {
					        Id = boardMapping.ArchiveLaneId,
					        Name = archiveLane.Title,
					        IsFirst = false,
					        IsLast = true
				        });
			        }
		        }
	        }

	        if (boardMapping.Types == null)
		        boardMapping.Types = new List<WorkItemType>();

	        // values in LaneToStatesMap are assumed to valid, as they were configured using valid values.
	        if(boardMapping.LaneToStatesMap==null)
	        {
		        Log.Fatal("An unexpected error occurred -- there is no valid lane-to-states mapping.");
	        }
        }
		protected override void OnStartFixture()
		{
			_testBoard = Test<Board>.Item;
			_mapping = Test<BoardMapping>.Item;
			_testCardAddResult1 = Test<CardAddResult>.Item;
			_testCardAddResult1.CardId = 1;
			_mapping.Identity.LeanKit = _testBoard.Id;
			_mapping.LaneToStatesMap.Add(1, new List<string> {"open"});
			TestConfig = Test<Configuration>.Item;
			TestConfig.PollingFrequency = 5000;
			TestConfig.Mappings = new List<BoardMapping> {_mapping};
		}
        private void CreateCardFromWorkItem(BoardMapping project, WorkItem workItem)
        {
            if (workItem == null) return;

            var boardId = project.Identity.LeanKit;

            var mappedCardType = workItem.LeanKitCardType(project);

            var laneId = project.LanesFromState(workItem.State).First();
            var card = new Card
                {
                    Active = true,
                    Title = workItem.Title,
                    Description = workItem.LeanKitDescription(GetTfsVersion()),
                    Priority = workItem.LeanKitPriority(),
                    TypeId = mappedCardType.Id,
                    TypeName = mappedCardType.Name,
                    LaneId = laneId,
                    ExternalCardID = workItem.Id.ToString(CultureInfo.InvariantCulture),
                    ExternalSystemName = ServiceName
                };

			if (workItem.Fields.Contains("Tags") && workItem.Fields["Tags"] != null && workItem.Fields["Tags"].Value != null)
			{
				card.Tags = workItem.Fields["Tags"].Value.ToString().Replace(";", ",");
			}

            if (project.TagCardsWithTargetSystemName && (card.Tags == null || !card.Tags.Contains(ServiceName))) 
			{
				if (string.IsNullOrEmpty(card.Tags))
					card.Tags = ServiceName;
				else
					card.Tags += "," + ServiceName;
			}

			if (_projectHyperlinkService != null)
			{
				card.ExternalSystemUrl = _projectHyperlinkService.GetWorkItemEditorUrl(workItem.Id).ToString();
			}

	        if (workItem.Fields != null && workItem.Fields.Contains("Assigned To"))
	        {
		        if (workItem.Fields["Assigned To"] != null && workItem.Fields["Assigned To"].Value != null)
		        {
			        var assignedUserId = CalculateAssignedUserId(boardId, workItem.Fields["Assigned To"].Value.ToString());
			        if (assignedUserId != null)
				        card.AssignedUserIds = new[] {assignedUserId.Value};
		        }
	        }

			if (workItem.Fields != null && workItem.Fields.Contains("Due Date"))
			{
				if (workItem.Fields["Due Date"] != null && workItem.Fields["Due Date"].Value != null)
				{
					DateTime tfsDueDate;
					var isDate = DateTime.TryParse(workItem.Fields["Due Date"].Value.ToString(), out tfsDueDate);
					if (isDate)
					{
						if (CurrentUser != null)
						{
							var dateFormat = CurrentUser.DateFormat ?? "MM/dd/yyyy";
							card.DueDate = tfsDueDate.ToString(dateFormat);
						}
					}
				}
			}

			if (workItem.Fields != null && (workItem.Fields.Contains("Original Estimate") || workItem.Fields.Contains("Story Points")))
			{
				if (workItem.Fields.Contains("Original Estimate") && workItem.Fields["Original Estimate"] != null && workItem.Fields["Original Estimate"].Value != null)
				{
					double cardSize;
					var isNumber = Double.TryParse(workItem.Fields["Original Estimate"].Value.ToString(), out cardSize);
					if (isNumber)
						card.Size = (int)cardSize;
				}
				else if (workItem.Fields.Contains("Story Points") && workItem.Fields["Story Points"] != null && workItem.Fields["Story Points"].Value != null)
				{
					double cardSize;
					var isNumber = Double.TryParse(workItem.Fields["Story Points"].Value.ToString(), out cardSize);
					if (isNumber)
						card.Size = (int) cardSize;
				}
			}

	        Log.Info("Creating a card of type [{0}] for work item [{1}] on Board [{2}] on Lane [{3}]", mappedCardType.Name, workItem.Id, boardId, laneId);

	        CardAddResult cardAddResult = null;

	        var tries = 0;
	        var success = false;
	        while (tries < 10 && !success)
	        {
		        if (tries > 0)
		        {
			        Log.Warn(String.Format("Attempting to create card for work item [{0}] attempt number [{1}]", workItem.Id,
			                                 tries));
					// wait 5 seconds before trying again
					Thread.Sleep(new TimeSpan(0, 0, 5));
		        }

		        try
		        {
			        cardAddResult = LeanKit.AddCard(boardId, card, "New Card From TFS Work Item");
			        success = true;
		        }
		        catch (Exception ex)
		        {
					Log.Error(ex, string.Format("An error occurred creating a new card for work item [{0}]", workItem.Id));
		        }
		        tries++;
	        }
	        card.Id = cardAddResult.CardId;

            Log.Info("Created a card [{0}] of type [{1}] for work item [{2}] on Board [{3}] on Lane [{4}]", card.Id, mappedCardType.Name, workItem.Id, boardId, laneId);
        }
Beispiel #50
0
 public static CardType LeanKitCardType(this Unfuddle.Ticket ticket, BoardMapping project)
 {
     return(CalculateLeanKitCardType(project, ""));
 }
Beispiel #51
0
        private void CreateCardFromItem(BoardMapping project, Issue issue)
        {
            if (issue == null)
            {
                return;
            }
            if (!project.CreateCards)
            {
                Log.Debug("CreateCards is disabled, skipping card creation.");
                return;
            }

            var boardId = project.Identity.LeanKit;

            var mappedCardType = issue.LeanKitCardType(project);
            var laneId         = project.LanesFromState(issue.State).First();
            var card           = new Card
            {
                Active             = true,
                Title              = issue.Title,
                Description        = issue.Body.SanitizeCardDescription(),
                Priority           = issue.LeanKitPriority(),
                TypeId             = mappedCardType.Id,
                TypeName           = mappedCardType.Name,
                LaneId             = laneId,
                ExternalCardID     = issue.Id + "|" + issue.Number,
                ExternalSystemName = ServiceName,
                ExternalSystemUrl  = string.Format(_externalUrlTemplate, project.Identity.Target, issue.Number),
                Index              = 9999
            };

            var assignedUserId = issue.LeanKitAssignedUserId(boardId, LeanKit);

            if (assignedUserId != null)
            {
                card.AssignedUserIds = new[] { assignedUserId.Value }
            }
            ;

            if (issue.Milestone != null && issue.Milestone.Due_On != null)
            {
                if (CurrentUser != null)
                {
                    var dateFormat = CurrentUser.DateFormat ?? "MM/dd/yyyy";
                    card.DueDate = issue.Milestone.Due_On.Value.ToString(dateFormat);
                }
            }

            if (issue.Labels != null && issue.Labels.Any())
            {
                card.Tags = string.Join(",", issue.Labels.Select(x => x.Name).ToList());
            }

            if ((card.Tags == null || !card.Tags.Contains(ServiceName)) && project.TagCardsWithTargetSystemName)
            {
                if (string.IsNullOrEmpty(card.Tags))
                {
                    card.Tags = ServiceName;
                }
                else
                {
                    card.Tags += "," + ServiceName;
                }
            }

            Log.Info("Creating a card of type [{0}] for Issue [{1}] on Board [{2}] on Lane [{3}]", mappedCardType.Name, issue.Number, boardId, laneId);

            CardAddResult cardAddResult = null;

            int  tries   = 0;
            bool success = false;

            while (tries < 10 && !success)
            {
                if (tries > 0)
                {
                    Log.Error(string.Format("Attempting to create card for issue [{0}] attempt number [{1}]", issue.Id,
                                            tries));
                    // wait 5 seconds before trying again
                    Thread.Sleep(new TimeSpan(0, 0, 5));
                }

                try {
                    cardAddResult = LeanKit.AddCard(boardId, card, "New Card From GitHub Issue");
                    success       = true;
                } catch (Exception ex) {
                    Log.Error(string.Format("An error occurred: {0} - {1} - {2}", ex.GetType(), ex.Message, ex.StackTrace));
                }
                tries++;
            }
            card.Id = cardAddResult.CardId;

            Log.Info("Created a card [{0}] of type [{1}] for Issue [{2}] on Board [{3}] on Lane [{4}]", card.Id, mappedCardType.Name, issue.Number, boardId, laneId);
        }
		private void CreateCardFromItem(BoardMapping project, Issue issue)
		{
			if (issue == null) return;

			var boardId = project.Identity.LeanKit;

			var mappedCardType = issue.LeanKitCardType(project);

			var validLanes = project.LanesFromState(issue.Fields.Status.Name);
			var laneId = validLanes.Any() ? validLanes.First() : project.DefaultCardCreationLaneId;

			var card = new Card
			{
				Active = true,
				Title = issue.Fields.Summary,
				Description = issue.Fields.Description.SanitizeCardDescription().JiraPlainTextToLeanKitHtml(),
				Priority = issue.LeanKitPriority(),
				TypeId = mappedCardType.Id,
				TypeName = mappedCardType.Name,
				LaneId = laneId,
				ExternalCardID = issue.Key,
				ExternalSystemName = ServiceName,
				ExternalSystemUrl = string.Format(_externalUrlTemplate, issue.Key)
			};

			var assignedUserId = issue.LeanKitAssignedUserId(boardId, LeanKit);
			if (assignedUserId != null)
				card.AssignedUserIds = new[] {assignedUserId.Value};

			if (issue.Fields != null && issue.Fields.DueDate != null && CurrentUser != null)
			{
				var dateFormat = CurrentUser.DateFormat ?? "MM/dd/yyyy";
				card.DueDate = issue.Fields.DueDate.Value.ToString(dateFormat, CultureInfo.InvariantCulture);
			}

			if (issue.Fields != null && issue.Fields.Labels != null && issue.Fields.Labels.Any())
			{
				card.Tags = string.Join(",", issue.Fields.Labels);
			}

			if ((card.Tags == null || !card.Tags.Contains(ServiceName)) && project.TagCardsWithTargetSystemName)
			{
				if (string.IsNullOrEmpty(card.Tags))
					card.Tags = ServiceName;
				else
					card.Tags += "," + ServiceName;
			}

			// TODO: Add size from the custom story points field.

			Log.Info("Creating a card of type [{0}] for issue [{1}] on Board [{2}] on Lane [{3}]", mappedCardType.Name,
				issue.Key, boardId, laneId);

			CardAddResult cardAddResult = null;

			int tries = 0;
			bool success = false;
			while (tries < 10 && !success)
			{
				if (tries > 0)
				{
					Log.Error(string.Format("Attempting to create card for work item [{0}] attempt number [{1}]",
						issue.Key, tries));
					// wait 5 seconds before trying again
					Thread.Sleep(new TimeSpan(0, 0, 5));
				}

				try
				{
					cardAddResult = LeanKit.AddCard(boardId, card, "New Card From Jira Issue");
					success = true;
				}
				catch (Exception ex)
				{
					Log.Error(string.Format("An error occurred: {0} - {1} - {2}", ex.GetType(), ex.Message,
						ex.StackTrace));
				}
				tries++;
			}
			card.Id = cardAddResult.CardId;

			Log.Info("Created a card [{0}] of type [{1}] for work item [{2}] on Board [{3}] on Lane [{4}]", card.Id,
				mappedCardType.Name, issue.Key, boardId, laneId);
		}
Beispiel #53
0
        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++;
            }
        }
        private void WorkItemUpdated(WorkItem workItem, Card card, BoardMapping project)
        {
            Log.Info("WorkItem [{0}] updated, comparing to corresponding card...", workItem.Id);

	        var boardId = project.Identity.LeanKit;

            // sync and save those items that are different (of title, description, priority)
            var saveCard = false;
            if (workItem.Title != card.Title)
            {
                card.Title = workItem.Title;
                saveCard = true;
            }

            var description = workItem.LeanKitDescription(GetTfsVersion());
            if (description != card.Description)
            {
                card.Description = description;
                saveCard = true;
            }

            var priority = workItem.LeanKitPriority();
            if(priority!= card.Priority)
            {
                card.Priority = priority;
                saveCard = true;
            }
            
            if(workItem.Fields!=null && 
				workItem.Fields.Contains("Tags") && 
				workItem.Fields["Tags"] != null && 
				workItem.Fields["Tags"].Value.ToString() != card.Tags)
            {
	            var tfsTags = workItem.Fields["Tags"].Value.ToString();
				// since we cannot set the tags in TFS we cannot blindly overwrite the LK tags 
				// with what is in TFS. Instead we can only add TFS tags to LK
				if (!string.IsNullOrEmpty(tfsTags))
				{
					var tfsTagsArr = tfsTags.Contains(',') ? tfsTags.Split(',') : tfsTags.Split(';');
					foreach (var tag in tfsTagsArr)
					{
						if (card.Tags != null && card.Tags.ToLowerInvariant().Contains(tag.ToLowerInvariant())) continue;
						if (string.IsNullOrEmpty(card.Tags))
							card.Tags = tag;
						else
							card.Tags += "," + tag;
						saveCard = true;
					}
				}
            }

			if (workItem.Fields != null && (workItem.Fields.Contains("Original Estimate") || workItem.Fields.Contains("Story Points"))) 
			{
				if (workItem.Fields.Contains("Original Estimate") && workItem.Fields["Original Estimate"] != null && workItem.Fields["Original Estimate"].Value != null) 
				{
					double cardSize;
					var isNumber = Double.TryParse(workItem.Fields["Original Estimate"].Value.ToString(), out cardSize);
					if (isNumber)
					{
						var size = (int) cardSize;
						if (card.Size != size)
						{
							card.Size = size;
							saveCard = true;
						}
					}
				} 
				else if (workItem.Fields.Contains("Story Points") && workItem.Fields["Story Points"] != null && workItem.Fields["Story Points"].Value != null) 
				{
					double cardSize;
					var isNumber = Double.TryParse(workItem.Fields["Story Points"].Value.ToString(), out cardSize);
					if (isNumber)
					{
						var size = (int)cardSize;
						if (card.Size != size) 
						{
							card.Size = size;
							saveCard = true;
						}
					}
				}
			}

			if ((card.Tags == null || !card.Tags.Contains(ServiceName)) && project.TagCardsWithTargetSystemName) 
			{
				if (string.IsNullOrEmpty(card.Tags))
					card.Tags = ServiceName;
				else
					card.Tags += "," + ServiceName;
				saveCard = true;
			}

            if(saveCard)
            {
                Log.Info("Updating card [{0}]", card.Id);
                LeanKit.UpdateCard(boardId, card);
            }

			// check the state of the work item
			// if we have the state mapped to a lane then check to see if the card is in that lane
			// if it is not in that lane then move it to that lane
	        if (!project.UpdateCardLanes || string.IsNullOrEmpty(workItem.State)) return;

	        // if card is already in archive lane then we do not want to move it to the end lane
	        // because it is effectively the same thing with respect to integrating with TFS
	        if (card.LaneId == project.ArchiveLaneId)
	        {
		        return;
	        }

			var laneIds = project.LanesFromState(workItem.State);

	        if (laneIds.Any())
	        {
		        if (!laneIds.Contains(card.LaneId))
		        {
					// first let's see if any of the lanes are sibling lanes, if so then 
					// we should be using one of them. So we'll limit the results to just siblings
					if (project.ValidLanes != null) {
						var siblingLaneIds = (from siblingLaneId in laneIds
											  let parentLane =
												  project.ValidLanes.FirstOrDefault(x =>
													  x.HasChildLanes &&
													  x.ChildLaneIds.Contains(siblingLaneId) &&
													  x.ChildLaneIds.Contains(card.LaneId))
											  where parentLane != null
											  select siblingLaneId).ToList();
						if (siblingLaneIds.Any())
							laneIds = siblingLaneIds;
					}

			        LeanKit.MoveCard(project.Identity.LeanKit, card.Id, laneIds.First(), 0, "Moved Lane From TFS Work Item");
		        }
	        }
        }
Beispiel #55
0
 protected override void CreateNewItem(Card card, BoardMapping boardMapping)
 {
     Log.Debug(String.Format("TODO: Create an Issue from Card [{0}]", card.Id));
 }
	    protected override void Synchronize(BoardMapping project)
        {
            Log.Debug("Polling TFS [{0}] for Work Items", project.Identity.TargetName);

			//query a project for new items   
	        var stateQuery = string.Format(" AND ({0})", String.Join(" or ", project.QueryStates.Select(x => "[System.State] = '" + x.Trim() + "'").ToList()));
	        var iterationQuery = "";
			if (!string.IsNullOrEmpty(project.IterationPath))
	        {
		        iterationQuery = string.Format(" AND [System.IterationPath] UNDER '{0}' ", project.IterationPath);
	        }

	        var queryAsOfDate = QueryDate.AddMilliseconds(Configuration.PollingFrequency*-1.5).ToString("o");

			string tfsQuery;
			if (!string.IsNullOrEmpty(project.Query)) 
			{
				tfsQuery = string.Format(project.Query, queryAsOfDate);
			} 
			else 
			{
				tfsQuery = String.Format(
					"[System.TeamProject] = '{0}' {1} {2} {3} and [System.ChangedDate] > '{4}'",
					 project.Identity.TargetName, iterationQuery, stateQuery, project.ExcludedTypeQuery, queryAsOfDate);
			}

		    var queryStr = string.Format("SELECT [System.Id], [System.WorkItemType]," +
		                                    " [System.State], [System.AssignedTo], [System.Title], [System.Description]" +
		                                    " FROM WorkItems " +
		                                    " WHERE {0}" +
		                                    " ORDER BY [System.TeamProject]", tfsQuery);
		    if (_projectCollectionWorkItemStore == null)
		    {
			    "Reconnecting to TFS...".Info();
				Init();
		    }
		    Query query;
		    try
		    {
			    query = new Query(_projectCollectionWorkItemStore, queryStr, null, false);
		    }
		    catch (Exception ex)
		    {
			    Log.Error("Error creating TFS query. {0} ", ex.Message);
				if (_projectCollectionWorkItemStore == null) Log.Error("Project Collection Work Item Store is null");
			    throw;
		    }
		    var cancelableAsyncResult = query.BeginQuery();

            var changedItems = query.EndQuery(cancelableAsyncResult);

			Log.Info("\nQuery [{0}] for changes after {1}", project.Identity.Target, queryAsOfDate);
			Log.Debug(queryStr);

		    foreach (WorkItem item in changedItems)
            {
                Log.Info("Work Item [{0}]: {1}, {2}, {3}",
                                  item.Id, item.Title, item.Fields["System.AssignedTo"].Value, item.State);

                // does this workitem have a corresponding card?
                var card = LeanKit.GetCardByExternalId(project.Identity.LeanKit, item.Id.ToString(CultureInfo.InvariantCulture));

                if (card == null || !card.ExternalSystemName.Equals(ServiceName, StringComparison.OrdinalIgnoreCase))
                {
                    Log.Debug("Creating new card for work item [{0}]", item.Id);
                    CreateCardFromWorkItem(project, item);
                }
                // TODO: else if Lane = defined end lane then update it in TFS (i.e. we missed the event)
                // call UpdateStateOfExternalWorkItem()
                else
                {
                    Log.Info("Previously created a card for work item[{0}]", item.Id);
                    if (project.UpdateCards)
                        WorkItemUpdated(item, card, project);
                    else
                        Log.Info("Skipped card update because 'UpdateCards' is disabled.");
                }
            }
            Log.Info("{0} item(s) queried.\n", changedItems.Count);            
        }
	    protected override void UpdateStateOfExternalItem(Card card, List<string> states, BoardMapping boardMapping)
	    {
			UpdateStateOfExternalItem(card, states, boardMapping, false);
	    }
		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++;
			}
		}
			public void TestUpdateStateOfExternalItem(Card card, List<string> laneStateMap, BoardMapping boardConfig)
			{
				base.UpdateStateOfExternalItem(card, laneStateMap, boardConfig, true);
			}
        protected void UpdateStateOfExternalItem(Card card, List<string> states, BoardMapping mapping, bool runOnlyOnce)
        {
	        if (!mapping.UpdateTargetItems) return;
			if (!card.ExternalSystemName.Equals(ServiceName, StringComparison.OrdinalIgnoreCase)) return;
			if (string.IsNullOrEmpty(card.ExternalCardID)) return;

			int workItemId;

			// use external card id to get the TFS work item
	        try
	        {
		        workItemId = Convert.ToInt32(card.ExternalCardID);
	        }
	        catch (Exception)
	        {
		        Log.Debug("Ignoring card [{0}] with missing external id value.", card.Id);
		        return;
	        }

	        if (states == null || states.Count == 0) return;

			var tries = 0;
			var 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}]", workItemId, tries));
					// wait 5 seconds before trying again
					Thread.Sleep(new TimeSpan(0, 0, 5));
				}

				Log.Debug("Attempting to retrieve work item [{0}]", workItemId);
				var workItemToUpdate = _projectCollectionWorkItemStore.GetWorkItem(workItemId);
				if (workItemToUpdate != null)
				{
					var initialState = workItemToUpdate.State;

					// iterate through the configured states until we find the one that works. 
					// Alternately we could do something with the validation results and check AllowedStates
					// may be able to figure out the issue and handle it
					// see http://bartwullems.blogspot.com/2012/04/tf237124-work-item-is-not-ready-to-save.html
					// according to docs the result should be a collection of Microsoft.TeamFoundation.WorkItemTracking.Client, not sure why it is an ArrayList
				    var ctr = 0;
					var valid = false;
					foreach (var st in states)
					{
						if (ctr > 0)
							workItemToUpdate = _projectCollectionWorkItemStore.GetWorkItem(workItemId);

						var attemptState = st;
						// Check for a workflow mapping to the closed state
						if (attemptState.Contains(">"))
						{
							var workflowStates = attemptState.Split('>');

							// check to see if the workitem is already in one of the workflow states
							var alreadyInState = workflowStates.FirstOrDefault(x => x.Trim().ToLowerInvariant() == workItemToUpdate.State.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 (var i = currentIndex + 1; i < workflowStates.Length; i++)
									{
										updatedWorkflowStates.Add(workflowStates[i]);
									}
									workflowStates = updatedWorkflowStates.ToArray();
								}
							}
                            //                                        UpdateStateOfExternalItem(card, new List<string>() { workflowState.Trim() }, mapping, runOnlyOnce);

							if (workflowStates.Length > 0) 
							{
								// if it is already in the final state then bail
								if (workItemToUpdate.State.ToLowerInvariant() == workflowStates.Last().ToLowerInvariant())
								{
									Log.Debug("WorkItem [{0}] is already in state [{1}]", workItemId, workItemToUpdate.State);
									return;
								}

								// check to make sure that the first state is valid
								// if it is then update the work item through the workflow
								// if not then just process the states from the config file as normal 
								workItemToUpdate.State = workflowStates[0];

								// check to see if the work item is already in any of the states
								// if so then skip those states
								if (workItemToUpdate.IsValid()) 
								{
									Log.Debug("Attempting to process WorkItem [{0}] through workflow of [{1}].", workItemId, attemptState);
									foreach (var workflowState in workflowStates)
									{
									    UpdateStateOfExternalItem(card, new List<string> {workflowState.Trim()}, mapping, runOnlyOnce);
									}
									// Get the work item again and check the current state. 
									// if it is not in the last state of the workflow then something went wrong
									// so reverse the updates we made and it will then try the next configuration workflow if there is one
									var updatedWorkItem = _projectCollectionWorkItemStore.GetWorkItem(workItemId);
									if (updatedWorkItem != null)
									{
										if (updatedWorkItem.State.ToLowerInvariant() == workflowStates.Last().ToLowerInvariant())
										{
											return;
										}

										// try to reverse the changes we've made
										Log.Debug("Attempted invalid workflow for WorkItem [{0}]. Attempting to reverse previous state changes.", workItemId);
										foreach (var workflowState in workflowStates.Reverse().Skip(1))
										{
										    UpdateStateOfExternalItem(card, new List<string> {workflowState.Trim()}, mapping, runOnlyOnce);
										}

										// now try to set it back whatever it was before
										Log.Debug("Attempted invalid workflow for WorkItem [{0}]. Setting state back to initial state of [{1}].", workItemId, initialState);
										UpdateStateOfExternalItem(card, new List<string> {initialState.Trim()}, mapping, runOnlyOnce );

										// set the current attempt to empty string so that it will not be valid and 
										// we'll try the next state (or workflow)
										attemptState = "";
									}
								}
							}
						}
						

						if (workItemToUpdate.State.Equals(attemptState, StringComparison.InvariantCultureIgnoreCase))
						{
							Log.Debug(string.Format("WorkItem [{0}] is already in state [{1}]", workItemId, workItemToUpdate.State));
							return;
						}

						if (!string.IsNullOrEmpty(attemptState))
						{
							workItemToUpdate.State = attemptState;
							valid = workItemToUpdate.IsValid();
						}

						ctr++;

						if (valid) break;
					}

					if (!valid)
					{
						Log.Warn("Unable to update WorkItem [{0}] to [{1}] because the state is invalid from the current state.", 
							workItemId, workItemToUpdate.State);
						return;
					}

					try
					{
						workItemToUpdate.Save();
						success = true;
						Log.Debug("Updated state for mapped WorkItem [{0}] to [{1}]", workItemId, workItemToUpdate.State);
					}
					catch (ValidationException ex)
					{
						Log.Warn("Unable to update WorkItem [{0}] to [{1}], ValidationException: {2}", workItemId, workItemToUpdate.State, ex.Message);
					}
					catch (Exception ex)
					{
						Log.Error(ex, string.Format("Unable to update WorkItem [{0}] to [{1}]", workItemId, workItemToUpdate.State));
					}
				}
				else
				{
					Log.Debug("Could not retrieve WorkItem [{0}] for updating state to [{1}]", workItemId, workItemToUpdate.State);
				}
				tries++;
			}
		}