Exemplo n.º 1
0
        private async Task <IEnumerable <int> > FetchRelatedWorkItems(TeamMember member, IEnumerable <VSTSProject> projects)
        {
            var date      = member.LastFetchDate == DateTime.MinValue ? DateTime.UtcNow.AddYears(-10) : member.LastFetchDate;
            var queryText = string.Format(WorkItemsQuery, member.Email, date.ToString("MM/dd/yyyy"));
            var query     = new ItemsQuery(queryText);

            var ids = new List <int>(1000);

            foreach (var project in projects)
            {
                _logger.LogInformation("Processing workitems for project '{0}'", project.Name);
                var wiqlEndPoint = VSTSApiUrl.Create(_config.Value.InstanceName)
                                   .ForWIQL(project.Name)
                                   .Build();
                var response = await _client.ExecutePost <WorkItemsQueryResponse>(wiqlEndPoint, query);

                ids.AddRange(response.WorkItems.Select(r => r.Id));
            }
            if (member.RelatedWorkItemIds != null)
            {
                member.RelatedWorkItemIds = member.RelatedWorkItemIds.Union(ids).Distinct();
            }
            else
            {
                member.RelatedWorkItemIds = ids;
            }
            return(ids);
        }
Exemplo n.º 2
0
        private async Task FetchUpdatesAndSave(IEnumerable <VSTSWorkItem> workItems)
        {
            _logger.LogInformation("Starting to fetch updates");
            foreach (var wi in workItems)
            {
                try
                {
                    var url = VSTSApiUrl.Create(_config.Value.InstanceName)
                              .ForWorkItems(wi.WorkItemId)
                              .WithSection("updates")
                              .Build();

                    var updates = await _client.ExecuteGet <ValueResponse <WorkItemUpdate> >(url);

                    wi.Updates = updates.Value.Where(u => !u.CanBeDiscarded);
                    await _repository.CreateOrUpdateAsync(wi, i => i.WorkItemId == wi.WorkItemId);
                }
                catch (Exception ex)
                {
                    _logger.LogWarning(ex, "Error updating workitem {WorkItemId}", wi.WorkItemId);
                    // TODO: Skipping ivalid workitems for now. Consider filtering updates to only include relevant fields.
                }
            }

            _logger.LogInformation("Finished fetching updates");
        }
Exemplo n.º 3
0
        private async Task <IEnumerable <VSTSWorkItem> > GetWorkItems(IEnumerable <int> changedWorkItems)
        {
            var          count            = (decimal)changedWorkItems.Count();
            const int    maxIdsPerRequest = 200;
            const string fieldsString     = "System.Id,System.WorkItemType,System.Title,System.AreaPath,System.ChangedDate,System.Tags,System.State,System.Reason," +
                                            "System.CreatedDate,Microsoft.VSTS.Common.ResolvedDate,Microsoft.VSTS.Common.ClosedDate,Microsoft.VSTS.Common.StateChangeDate," +
                                            "Microsoft.VSTS.Scheduling.OriginalEstimate,Microsoft.VSTS.Scheduling.CompletedWork,Microsoft.VSTS.Scheduling.RemainingWork";
            var iterations = Math.Ceiling(count / maxIdsPerRequest);
            var wis        = new List <VSTSWorkItem>((int)count);

            _logger.LogInformation("Starting workitems fetch. Total count: {0}, iterations needed: {1}", count, iterations);
            for (int i = 0; i < iterations; i++)
            {
                _logger.LogInformation("Starting iteration {0}", i);
                var idsToQuery = string.Join(',', changedWorkItems.Skip(i * maxIdsPerRequest).Take(maxIdsPerRequest));
                var wiQuery    = VSTSApiUrl.Create(_config.Value.InstanceName)
                                 .ForWorkItemsBatch(idsToQuery)
                                 .WithQueryParameter("fields", fieldsString)
                                 .Build();

                var workItemsResponse = await _client.ExecuteGet <ValueResponse <VSTSWorkItem> >(wiQuery);

                wis.AddRange(workItemsResponse.Value);
            }

            return(wis);
        }
Exemplo n.º 4
0
        public async Task <IActionResult> OnPost()
        {
            if (!ModelState.IsValid)
            {
                return(Page());
            }

            var identityUrl = VSTSApiUrl.Create(_config.Value.InstanceName, "vssps")
                              .ForIdentities()
                              .WithQueryParameter("searchFilter", "General")
                              .WithQueryParameter("filterValue", Member.Email)
                              .Build("4.0");

            var identities = await _client.ExecuteGet <ValueBasedResponse <IdentityResponse> >(identityUrl);

            var users = identities.Value
                        .Where(u => u.IsActive)
                        .ToList();

            if (users.Count == 1)
            {
                var member = new TeamMember
                {
                    Id          = users[0].Id,
                    Email       = Member.Email,
                    DisplayName = Member.DisplayName,
                    TeamName    = Member.TeamName
                };

                await _repository.CreateOrUpdateAsync(member);

                return(RedirectToPage("TeamMembers"));
            }

            if (users.Count == 0)
            {
                var message = $"No active users found with email '{Member.Email}'; Count including inactive: {identities.Count}";
                _logger.LogWarning(message);
                ModelState.AddModelError($"{nameof(Member)}.{nameof(Member.Email)}", $"No active users found with email '{Member.Email}'");
            }
            else if (users.Count > 1)
            {
                var usersList = string.Join("; ", users.Select(u => $"{u.DisplayName} [{u.Id}]"));
                var message   = $"More than one active user found with email '{Member.Email}'. List: {usersList}";
                _logger.LogWarning(message);
                ModelState.AddModelError($"{nameof(Member)}.{nameof(Member.Email)}", $"More than one active user found with email '{Member.Email}'");
            }

            return(Page());
        }
Exemplo n.º 5
0
        private async Task <QueryDiff> GetQueryHistory(Guid queryId, string project, DateTime from, DateTime to)
        {
            var queryUrl = VSTSApiUrl.Create(_config.Value.InstanceName)
                           .ForQueries(project, queryId.ToString())
                           .WithQueryParameter("$expand", "wiql")
                           .Build();
            var wiqlUrl = VSTSApiUrl.Create(_config.Value.InstanceName)
                          .ForWIQL(project)
                          .Build();

            var query = await _client.ExecuteGet <VSTSQuery>(queryUrl);

            var days = (int)to.Date.Subtract(from.Date).TotalDays;
            var diff = new QueryDiff()
            {
                States = new List <QueryState>(days)
            };

            for (int i = 0; i < days; i++)
            {
                var date          = from.Date.AddDays(i);
                var modifiedQuery = $"{query.Wiql} ASOF '{date.ToString("yyyy-MM-dd")}'";
                var result        = await _client.ExecutePost <WorkItemsQueryResponse>(wiqlUrl, new ItemsQuery(modifiedQuery));

                var previous = diff.States.LastOrDefault();
                var current  = new QueryState
                {
                    Date       = date,
                    ItemsCount = result.Count
                };

                if (previous != null)
                {
                    current.Trend = current.ItemsCount - previous.ItemsCount;
                }
                diff.States.Add(current);
            }

            return(diff);
        }