diff --git a/Octokit.Tests.Integration/Clients/IssuesClientTests.cs b/Octokit.Tests.Integration/Clients/IssuesClientTests.cs index 1a68ef0f..363c12b7 100644 --- a/Octokit.Tests.Integration/Clients/IssuesClientTests.cs +++ b/Octokit.Tests.Integration/Clients/IssuesClientTests.cs @@ -273,6 +273,84 @@ public class IssuesClientTests : IDisposable Assert.Null(updatedIssue.Milestone); } + [IntegrationTest] + public async Task DoesNotChangeLabelsByDefault() + { + var owner = _repository.Owner.Login; + + await _issuesClient.Labels.Create(owner, _repository.Name, new NewLabel("something", "FF0000")); + + var newIssue = new NewIssue("A test issue1") + { + Body = "A new unassigned issue", + }; + newIssue.Labels.Add("something"); + + var issue = await _issuesClient.Create(owner, _repository.Name, newIssue); + + var issueUpdate = issue.ToUpdate(); + + var updatedIssue = await _issuesClient.Update(owner, _repository.Name, issue.Number, issueUpdate); + + Assert.Equal(1, updatedIssue.Labels.Count); + } + + [IntegrationTest] + public async Task CanUpdateLabelForAnIssue() + { + var owner = _repository.Owner.Login; + + // create some labels + await _issuesClient.Labels.Create(owner, _repository.Name, new NewLabel("something", "FF0000")); + await _issuesClient.Labels.Create(owner, _repository.Name, new NewLabel("another thing", "0000FF")); + + // setup us the issue + var newIssue = new NewIssue("A test issue1") + { + Body = "A new unassigned issue", + }; + newIssue.Labels.Add("something"); + + var issue = await _issuesClient.Create(owner, _repository.Name, newIssue); + + // update the issue + var issueUpdate = issue.ToUpdate(); + issueUpdate.AddLabel("another thing"); + + var updatedIssue = await _issuesClient.Update(owner, _repository.Name, issue.Number, issueUpdate); + + Assert.Equal("another thing", updatedIssue.Labels[0].Name); + } + + [IntegrationTest] + public async Task CanClearLabelsForAnIssue() + { + var owner = _repository.Owner.Login; + + // create some labels + await _issuesClient.Labels.Create(owner, _repository.Name, new NewLabel("something", "FF0000")); + await _issuesClient.Labels.Create(owner, _repository.Name, new NewLabel("another thing", "0000FF")); + + // setup us the issue + var newIssue = new NewIssue("A test issue1") + { + Body = "A new unassigned issue", + }; + newIssue.Labels.Add("something"); + newIssue.Labels.Add("another thing"); + + var issue = await _issuesClient.Create(owner, _repository.Name, newIssue); + Assert.Equal(2, issue.Labels.Count); + + // update the issue + var issueUpdate = issue.ToUpdate(); + issueUpdate.ClearLabels(); + + var updatedIssue = await _issuesClient.Update(owner, _repository.Name, issue.Number, issueUpdate); + + Assert.Empty(updatedIssue.Labels); + } + public void Dispose() { Helper.DeleteRepo(_repository); diff --git a/Octokit.Tests/Models/IssueTest.cs b/Octokit.Tests/Models/IssueTest.cs index ed3626df..a663c1ba 100644 --- a/Octokit.Tests/Models/IssueTest.cs +++ b/Octokit.Tests/Models/IssueTest.cs @@ -257,11 +257,9 @@ public class IssueTest var update = issue.ToUpdate(); - Assert.Equal("bug", update.Labels.Single()); + Assert.Null(update.Labels); Assert.Equal(1, update.Milestone.GetValueOrDefault()); Assert.Equal("octocat", update.Assignee); } } } - - diff --git a/Octokit/Models/Request/IssueUpdate.cs b/Octokit/Models/Request/IssueUpdate.cs index cbeedb9a..1a17bc48 100644 --- a/Octokit/Models/Request/IssueUpdate.cs +++ b/Octokit/Models/Request/IssueUpdate.cs @@ -9,13 +9,8 @@ namespace Octokit [DebuggerDisplay("{DebuggerDisplay,nq}")] public class IssueUpdate { - public IssueUpdate() - { - Labels = new List(); - } - /// - /// Title of the milestone (required) + /// Title of the issue (required) /// public string Title { get; set; } @@ -66,7 +61,26 @@ namespace Octokit public void AddLabel(string name) { + // lazily create the label array + if (Labels == null) + { + Labels = new List(); + } + Labels.Add(name); } + + public void ClearLabels() + { + // lazily create the label array + if (Labels == null) + { + Labels = new List(); + } + else + { + Labels.Clear(); + } + } } } diff --git a/Octokit/Models/Request/RepositoryIssueRequest.cs b/Octokit/Models/Request/RepositoryIssueRequest.cs index f125afd4..8d01e19b 100644 --- a/Octokit/Models/Request/RepositoryIssueRequest.cs +++ b/Octokit/Models/Request/RepositoryIssueRequest.cs @@ -22,9 +22,6 @@ namespace Octokit /// /// The user that created the issue /// - /// - /// Specify "none" for issues with no assigned user - /// public string Creator { get; set; } /// diff --git a/Octokit/Models/Response/Issue.cs b/Octokit/Models/Response/Issue.cs index 28140ce4..ae68304a 100644 --- a/Octokit/Models/Response/Issue.cs +++ b/Octokit/Models/Response/Issue.cs @@ -127,11 +127,6 @@ namespace Octokit Title = Title }; - foreach (var label in Labels.Select(l => l.Name)) - { - issueUpdate.Labels.Add(label); - } - return issueUpdate; } } diff --git a/docs/issues.md b/docs/issues.md new file mode 100644 index 00000000..1a73674a --- /dev/null +++ b/docs/issues.md @@ -0,0 +1,121 @@ +# Working with Issues + +There's three typical operations you have available when working +with issues - viewing, creating or editing issues. + +### Get All + +If you want to view all assigned, open issues against repositories you belong to +(either you own them, or you belong to a team or organization), use this +method: + +``` +var issues = await client.Issue.GetAllForCurrent(); +``` + +If you want to skip organization repositories, you can instead use this +rather verbose method: + +``` +var issues = await client.Issue.GetAllForOwnedAndMemberRepositories(); +``` + +If you know the specific repository, just invoke that: + +``` +var issuesForOctokit = await client.Issue.GetForRepository("octokit", "octokit.net"); +``` + +### Filtering + +Each of these methods has an overload which takes a parameter to filter results. + +The simplest request is `IssueRequest` which has these options: + + - `Filter` - specify which issues to display - by default it will display issues assigned to you + - `State` - by default it will display open issues, you can specify closed or all issues + - `Labels` - specify a set of labels to include + - `SortProperty` - sort by when the issue was created, when it was updated, or comment count + - `SortDirection` - whether to sort in ascending or descending fashion + - `Since` - ignore issues before a specific date + +For example, this is how you could find all issues updated in the past two weeks: + +``` +var recently = new IssueRequest +{ + Filter = IssueFilter.All, + State = ItemState.All, + Since = DateTimeOffset.Now.Subtract(TimeSpan.FromDays(14)) +}; +var issues = await client.Issue.GetAllForCurrent(recently); +``` + +`RepositoryIssueRequest` extends `IssueRequest` and adds these options: + + - `Milestone` - use `*` for any issue in a milestone, "none" for issues not assigned to a milestone + - `Assignee` - specify the GitHub username, or "none" for unassigned issues + - `Creator` - specify the GitHub username + - `Mentioned` - specify the GitHub username + +For example, to find all issues which need to be prioritized: + +``` +var shouldPrioritize = new RepositoryIssueRequest +{ + Assignee = "none", + Milestone = "none", + Filter = IssueFilter.All +}; +var issues = await client.Issue.GetForRepository("octokit", "octokit.net", shouldPrioritize); +``` + +### Create + +At a minimum, you need to specify the title: + +``` +var createIssue = new NewIssue("this thing doesn't work"); +var issue = await _issuesClient.Create("octokit", "octokit.net", createIssue); +``` + +`Create` returns a `Task` which represents the created issue. + +There's also a number of additional fields: + + - `Body` - details about the issue (Markdown) + - `Assignee` - the GitHub user to associate with the issue + - `Milestone` - the milestone id to assign the issue to + - `Labels` - a collection of labels to assign to the issue + +Note that `Milestones` and `Labels` need to exist in the repository before +creating the issue. Refer to the [Milestones](https://github.com/octokit/octokit.net/blob/master/docs/milestones.md) +and [Labels](https://github.com/octokit/octokit.net/blob/master/docs/labels.md) +sections for more details. + +### Update + +You can either hold the new issue in memory, or use the id to fetch the issue +later: + +``` +var issue = await client.Issue.Get("octokit", "octokit.net", 405); +``` + +With this issue, you can transform it into an `IssueUpdate` using the extension method: + +``` +var update = issue.ToUpdate(); +``` + +This creates an `IssueUpdate` which lets you specify the neccessary changes. +Label changes probably requires some explanation: + + - by default, no labels are set in an `IssueUpdate` - this is to indicate + to the server that no change is necessary when doing the update + - to set a new label as part of the update, call `AddLabel()` specifying + the name of the new label + - to remove all labels as part of the update, call `ClearLabels()` + +If you're trying to populate the `Labels` collection by hand, you might hit +some exceptional behaviour due to these rules. diff --git a/docs/labels.md b/docs/labels.md new file mode 100644 index 00000000..b5b7c41a --- /dev/null +++ b/docs/labels.md @@ -0,0 +1 @@ +# Working with Issue Labels \ No newline at end of file diff --git a/docs/milestones.md b/docs/milestones.md new file mode 100644 index 00000000..5454c5a4 --- /dev/null +++ b/docs/milestones.md @@ -0,0 +1 @@ +# Working with Milestones \ No newline at end of file