/// <inheritdoc />
        public async Task<IEnumerable<WorkItem>> GetWorkItemsAsync(string project, WorkItemsQuery query, CancellationToken cancellationToken = default(CancellationToken))
        {
            var queryResult = await ExecuteQueryAsync(project, query, cancellationToken);
            int[] ids;
            switch (queryResult)
            {
                case FlatWorkItemsQueryResult flat:
                    ids = GetWorkitemIdsFromQuery(flat);
                    break;
                case HierarchicalWorkItemsQueryResult tree:
                    ids = GetWorkitemIdsFromQuery(tree);
                    break;
                default:
                    throw new NotSupportedException($"Query result is of not supported type.");
            }

            if (!ids.Any())
                return Enumerable.Empty<WorkItem>();

            var fieldsString = string.Join(",", queryResult.Columns.Select(c => c.ReferenceName));
            var idsString = string.Join(",", ids);
            var url = VstsUrlBuilder.Create(_instanceName)
                .ForWorkItemsBatch(idsString, project)
                .WithQueryParameter("fields", fieldsString)
                .Build();

            var workitemsResponse = await _httpClient.ExecuteGet<CollectionResponse<WorkItem>>(url, cancellationToken);

            if (workitemsResponse == null)
                return Enumerable.Empty<WorkItem>();

            return workitemsResponse.Value;
        }
        /// <inheritdoc />
        public async Task <IEnumerable <WorkItem> > GetWorkItemsAsync(int[] ids, DateTime?asOf = null, string[] fields = null, CancellationToken cancellationToken = default(CancellationToken))
        {
            ThrowIfArgumentNull(ids, nameof(ids));

            if (!ids.Any())
            {
                return(Enumerable.Empty <WorkItem>());
            }

            var result       = new List <WorkItem>(ids.Length);
            var fieldsString = fields != null?string.Join(",", fields) : string.Empty;

            var asOfString = asOf.HasValue ? asOf.Value.ToString("u") : string.Empty;
            var batchSize  = Configuration.WorkitemsBatchSize;
            var batches    = Math.Ceiling((decimal)ids.Length / batchSize);

            for (int i = 0; i < batches; i++)
            {
                var idsString = string.Join(",", ids.Skip(i * batchSize).Take(batchSize));
                var url       = VstsUrlBuilder.Create(_instanceName)
                                .ForWorkItemsBatch(idsString)
                                .WithQueryParameterIfNotEmpty("fields", fieldsString)
                                .WithQueryParameterIfNotEmpty("asOf", asOfString)
                                .Build();

                var response = await _httpClient.ExecuteGet <CollectionResponse <WorkItem> >(url, cancellationToken);

                result.AddRange(response?.Value ?? Enumerable.Empty <WorkItem>());
            }

            return(result);
        }
        /// <inheritdoc />
        public async Task <bool> DeleteWorkItemAsync(int id, bool destroy = false, CancellationToken cancellationToken = default(CancellationToken))
        {
            var url = VstsUrlBuilder.Create(_instanceName)
                      .ForWorkItems(id)
                      .WithQueryParameterIfNotDefault("destroy", destroy)
                      .Build();

            await _httpClient.ExecuteDelete <WorkItemDeleteResponse>(url, cancellationToken);

            return(true);
        }
        /// <inheritdoc />
        public async Task<IEnumerable<WorkItemUpdate>> GetWorkItemUpdatesAsync(int workitemId, CancellationToken cancellationToken = default(CancellationToken))
        {
            var url = VstsUrlBuilder.Create(_instanceName)
                .ForWorkItems(workitemId)
                .WithSection("updates")
                .Build();

            var result = await _httpClient.ExecuteGet<CollectionResponse<WorkItemUpdate>>(url, cancellationToken);
            if (result == null)
                return Enumerable.Empty<WorkItemUpdate>();

            return result.Value;
        }
        /// <inheritdoc />
        public async Task<bool> DeleteWorkItemAsync(string project, int id, bool destroy = false, CancellationToken cancellationToken = default(CancellationToken))
        {
            ThrowIfArgumentNullOrEmpty(project, nameof(project));

            var url = VstsUrlBuilder.Create(_instanceName)
                    .WithSection(project)
                    .ForWorkItems(id)
                    .WithQueryParameterIfNotDefault("destroy", destroy)
                    .Build();

            await _httpClient.ExecuteDelete<WorkItemDeleteResponse>(url, cancellationToken);
            return true;
        }
        /// <inheritdoc />
        public async Task <WorkItem> GetWorkItemAsync(int workItemId, DateTime?asOf = null, string[] fields = null, CancellationToken cancellationToken = default(CancellationToken))
        {
            var fieldsString = fields != null?string.Join(",", fields) : string.Empty;

            var asOfString = asOf.HasValue ? asOf.Value.ToString("u") : string.Empty;

            var url = VstsUrlBuilder.Create(_instanceName)
                      .ForWorkItems(workItemId)
                      .WithQueryParameterIfNotEmpty("fields", fieldsString)
                      .WithQueryParameterIfNotEmpty("asOf", asOfString)
                      .Build();

            return(await _httpClient.ExecuteGet <WorkItem>(url, cancellationToken));
        }
        /// <inheritdoc />
        public async Task <WorkItem> UpdateWorkItemAsync(UpdateWorkitemRequest request, CancellationToken cancellationToken = default(CancellationToken))
        {
            ThrowIfArgumentNull(request, nameof(request));
            ThrowIfArgumentNull(request.Id, nameof(request.Id));

            var url = VstsUrlBuilder.Create(_instanceName)
                      .ForWorkItems()
                      .WithSection(request.Id.Value.ToString())
                      .Build();

            var result = await _httpClient.ExecutePatch <WorkItem>(url, request.Updates.ToArray(), Constants.JsonPatchMimeType, cancellationToken);

            return(result);
        }
        /// <inheritdoc />
        public async Task <IEnumerable <PullRequestThread> > GetPullRequestThreadsAsync(string project, string repository, int pullRequestId, CancellationToken cancellationToken = default(CancellationToken))
        {
            ThrowIfArgumentNullOrEmpty(project, nameof(project));
            ThrowIfArgumentNullOrEmpty(repository, nameof(repository));

            var threadsUrl = VstsUrlBuilder.Create(_instanceName)
                             .ForPullRequests(project, repository)
                             .WithSection(pullRequestId.ToString())
                             .WithSection("threads")
                             .Build();

            var threadsResponse = await _httpClient.ExecuteGet <CollectionResponse <PullRequestThread> >(threadsUrl, cancellationToken);

            return(threadsResponse?.Value ?? Enumerable.Empty <PullRequestThread>());
        }
        /// <inheritdoc />
        public async Task <IEnumerable <PullRequest> > GetPullRequestsAsync(string project, string repository, PullRequestQuery query, CancellationToken cancellationToken = default(CancellationToken))
        {
            ThrowIfArgumentNullOrEmpty(project, nameof(project));
            ThrowIfArgumentNullOrEmpty(repository, nameof(repository));

            if (query == null)
            {
                query = PullRequestQuery.None;
            }

            var haveMorePullRequests = true;
            var skip            = 0;
            var allPullRequests = new List <PullRequest>();

            while (haveMorePullRequests)
            {
                var url = VstsUrlBuilder.Create(_instanceName)
                          .ForPullRequests(project, repository)
                          .WithQueryParameterIfNotEmpty("searchCriteria.status", query.Status)
                          .WithQueryParameterIfNotEmpty("searchCriteria.reviewerId", query.ReviewerId)
                          .WithQueryParameterIfNotEmpty("searchCriteria.creatorId", query.CreatorId)
                          .WithQueryParameter("$skip", skip)
                          .Build();

                var pullRequestsResponse = await _httpClient.ExecuteGet <CollectionResponse <PullRequest> >(url, cancellationToken);

                var pullRequests = pullRequestsResponse?.Value ?? Enumerable.Empty <PullRequest>();

                haveMorePullRequests = pullRequests.Any() && (!query.CreatedAfter.HasValue || pullRequests.Min(p => p.CreationDate) >= query.CreatedAfter);

                if (query.CreatedAfter.HasValue)
                {
                    pullRequests = pullRequests.Where(p => p.CreationDate >= query.CreatedAfter);
                }

                if (query.CustomFilter != null)
                {
                    pullRequests = pullRequests.Where(query.CustomFilter);
                }

                allPullRequests.AddRange(pullRequests);

                skip = allPullRequests.Count;
            }

            return(allPullRequests);
        }
        /// <inheritdoc />
        public async Task<WorkItemsQueryResult> ExecuteQueryAsync(string project, WorkItemsQuery query, CancellationToken cancellationToken = default(CancellationToken))
        {
            ThrowIfArgumentNullOrEmpty(project);
            ThrowIfArgumentNull(query, nameof(query));
            ThrowIfArgumentNullOrEmpty(_instanceName, nameof(_instanceName));
            ThrowIfArgumentNullOrEmpty(query.Query, "Query cannot be empty.");

            var url = VstsUrlBuilder.Create(_instanceName)
                .ForWIQL(project)
                .Build(Constants.CurrentWorkItemsApiVersion);

            _logger.LogDebug("Requesting {0}", url);

            if (query.IsHierarchical)
                return await _httpClient.ExecutePost<HierarchicalWorkItemsQueryResult>(url, query, cancellationToken);

            return await _httpClient.ExecutePost<FlatWorkItemsQueryResult>(url, query, cancellationToken);
        }
        /// <inheritdoc />
        public async Task<IEnumerable<WorkItem>> GetWorkItemsAsync(string project, int[] ids, DateTime? asOf = null, string[] fields = null, CancellationToken cancellationToken = default(CancellationToken))
        {
            ThrowIfArgumentNullOrEmpty(project, nameof(project));
            ThrowIfArgumentNull(ids, nameof(ids));

            if (!ids.Any())
                return Enumerable.Empty<WorkItem>();

            var fieldsString = fields != null ? string.Join(",", fields) : string.Empty;
            var asOfString = asOf.HasValue ? asOf.Value.ToString("u") : string.Empty;
            var idsString = string.Join(",", ids);
            var url = VstsUrlBuilder.Create(_instanceName)
                .ForWorkItemsBatch(idsString, project)
                .WithQueryParameterIfNotEmpty("fields", fieldsString)
                .WithQueryParameterIfNotEmpty("asOf", asOfString)
                .Build();

            var response = await _httpClient.ExecuteGet<CollectionResponse<WorkItem>>(url, cancellationToken);
            return response?.Value ?? Enumerable.Empty<WorkItem>();
        }
        /// <inheritdoc />
        public async Task<WorkItem> CreateWorkItemAsync(string project, string type, WorkItem item, CancellationToken cancellationToken = default(CancellationToken))
        {
            ThrowIfArgumentNullOrEmpty(project, nameof(project));
            ThrowIfArgumentNullOrEmpty(type, nameof(type));
            ThrowIfArgumentNull(item, nameof(item));

            var updateRequest = new UpdateWorkitemRequest();
            foreach (var field in item.Fields)
            {
                updateRequest.AddFieldValue(field.Key, field.Value);
            }

            var url = VstsUrlBuilder.Create(_instanceName)
               .WithSection(project)
               .ForWorkItems()
               .WithSection($"${type}")
               .Build();

            var result = await _httpClient.ExecutePost<WorkItem>(url, updateRequest.Updates.ToArray(), Constants.JsonPatchMimeType, cancellationToken);
            return result;
        }
Beispiel #13
0
        public void ComposeUrlWithCustomBaseAddress(string baseAddress)
        {
            var result = VstsUrlBuilder.Create(new Uri(baseAddress)).Build();

            result.Should().Be($"{baseAddress.Trim('/')}?api-version={Constants.CurrentWorkItemsApiVersion}");
        }
Beispiel #14
0
        public void ComposeOnlineUrlWihSubdomainAndInstanceName()
        {
            var result = VstsUrlBuilder.Create(instance: "foo", subDomain: "bar.buzz").Build();

            result.Should().Be($"https://foo.bar.buzz.visualstudio.com?api-version={Constants.CurrentWorkItemsApiVersion}");
        }
Beispiel #15
0
        public void ComposeOnlineUrlWithInstanceName()
        {
            var result = VstsUrlBuilder.Create("foo").Build();

            result.Should().Be($"https://foo.visualstudio.com?api-version={Constants.CurrentWorkItemsApiVersion}");
        }