/// <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; }
public void ComposeUrlWithCustomBaseAddress(string baseAddress) { var result = VstsUrlBuilder.Create(new Uri(baseAddress)).Build(); result.Should().Be($"{baseAddress.Trim('/')}?api-version={Constants.CurrentWorkItemsApiVersion}"); }
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}"); }
public void ComposeOnlineUrlWithInstanceName() { var result = VstsUrlBuilder.Create("foo").Build(); result.Should().Be($"https://foo.visualstudio.com?api-version={Constants.CurrentWorkItemsApiVersion}"); }