Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions Octokit.Tests.Integration/Clients/IssuesClientTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,46 @@ public async Task CanCreateRetrieveAndCloseIssue()
}
}

[IntegrationTest]
public async Task CanCreateCloseAndReopenIssue()
{
var newIssue = new NewIssue("a test issue") { Body = "A new unassigned issue" };
newIssue.Labels.Add("test");
newIssue.Assignees.Add(_context.RepositoryOwner);

var issue = await _issuesClient.Create(_context.RepositoryOwner, _context.RepositoryName, newIssue);
Assert.NotNull(issue);
Assert.True(issue.Assignees.All(x => x.Login == _context.RepositoryOwner));

var retrieved = await _issuesClient.Get(_context.RepositoryOwner, _context.RepositoryName, issue.Number);
Assert.NotNull(retrieved);
Assert.True(retrieved.Assignees.Count == 1);
Assert.True(retrieved.Assignees[0].Login == _context.RepositoryOwner);

var update = retrieved.ToUpdate();
update.State = ItemState.Closed;
update.StateReason = ItemStateReason.NotPlanned;

var closed = await _issuesClient.Update(_context.RepositoryOwner, _context.RepositoryName, issue.Number, update);
Assert.NotNull(closed);
Assert.Equal(ItemState.Closed, closed.State);
Assert.Equal(ItemStateReason.NotPlanned, closed.StateReason);

retrieved = await _issuesClient.Get(_context.RepositoryOwner, _context.RepositoryName, issue.Number);
Assert.NotNull(retrieved);
Assert.Equal(ItemState.Closed, retrieved.State);
Assert.Equal(ItemStateReason.NotPlanned, retrieved.StateReason);

update = retrieved.ToUpdate();
update.State = ItemState.Open;
update.StateReason = ItemStateReason.Reopened;

var reopened = await _issuesClient.Update(_context.RepositoryOwner, _context.RepositoryName, issue.Number, update);
Assert.NotNull(reopened);
Assert.Equal(ItemState.Open, reopened.State);
Assert.Equal(ItemStateReason.Reopened, reopened.StateReason);
}

[IntegrationTest]
public async Task CanCreateRetrieveAndCloseIssueWithRepositoryId()
{
Expand Down
2 changes: 2 additions & 0 deletions Octokit.Tests/Models/IssueTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ public void CreatesAnIssueUpdateRequestObject()
""html_url"": ""https://github.com/octocat/Hello-World/issues/1347"",
""number"": 1347,
""state"": ""open"",
""state_reason"": ""reopened"",
""title"": ""Found a bug"",
""body"": ""I'm having a problem with this."",
""user"": {
Expand Down Expand Up @@ -283,6 +284,7 @@ public void CreatesAnIssueUpdateRequestObject()
Assert.NotNull(update.Labels);
Assert.Equal(1, update.Milestone.GetValueOrDefault());
Assert.Equal("octocat", update.Assignees.FirstOrDefault());
Assert.Equal(ItemStateReason.Reopened, update.StateReason.GetValueOrDefault());
}
}
}
24 changes: 24 additions & 0 deletions Octokit/Models/Request/IssueRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,30 @@ public enum ItemState
Closed
}

/// <summary>
/// The reason for the state change.
/// </summary>
public enum ItemStateReason
{
/// <summary>
/// Item closed as completed.
/// </summary>
[Parameter(Value = "completed")]
Completed,

/// <summary>
/// Item closed as unplanned.
/// </summary>
[Parameter(Value = "not_planned")]
NotPlanned,

/// <summary>
/// Item reopened.
/// </summary>
[Parameter(Value = "reopened")]
Reopened
}

/// <summary>
/// The available properties to sort issues by.
/// </summary>
Expand Down
7 changes: 6 additions & 1 deletion Octokit/Models/Request/IssueUpdate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ public class IssueUpdate
/// </summary>
public ItemState? State { get; set; }

/// <summary>
/// The reason for the state change. Ignored unless <see cref="State"/> is changed.
/// </summary>
public ItemStateReason? StateReason { get; set; }

internal string DebuggerDisplay
{
get
Expand Down Expand Up @@ -157,4 +162,4 @@ public void RemoveLabel(string name)
}
}
}
}
}
10 changes: 9 additions & 1 deletion Octokit/Models/Response/Issue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public class Issue
{
public Issue() { }

public Issue(string url, string htmlUrl, string commentsUrl, string eventsUrl, int number, ItemState state, string title, string body, User closedBy, User user, IReadOnlyList<Label> labels, User assignee, IReadOnlyList<User> assignees, Milestone milestone, int comments, PullRequest pullRequest, DateTimeOffset? closedAt, DateTimeOffset createdAt, DateTimeOffset? updatedAt, int id, string nodeId, bool locked, Repository repository, ReactionSummary reactions, LockReason? activeLockReason)
public Issue(string url, string htmlUrl, string commentsUrl, string eventsUrl, int number, ItemState state, string title, string body, User closedBy, User user, IReadOnlyList<Label> labels, User assignee, IReadOnlyList<User> assignees, Milestone milestone, int comments, PullRequest pullRequest, DateTimeOffset? closedAt, DateTimeOffset createdAt, DateTimeOffset? updatedAt, int id, string nodeId, bool locked, Repository repository, ReactionSummary reactions, LockReason? activeLockReason, ItemStateReason? stateReason)
{
Id = id;
NodeId = nodeId;
Expand All @@ -39,6 +39,7 @@ public Issue(string url, string htmlUrl, string commentsUrl, string eventsUrl, i
Repository = repository;
Reactions = reactions;
ActiveLockReason = activeLockReason;
StateReason = stateReason;
}

/// <summary>
Expand Down Expand Up @@ -81,6 +82,11 @@ public Issue(string url, string htmlUrl, string commentsUrl, string eventsUrl, i
/// </summary>
public StringEnum<ItemState> State { get; private set; }

/// <summary>
/// The reason for the state change. Ignored unless <see cref="State"/> is changed.
/// </summary>
public StringEnum<ItemStateReason>? StateReason { get; private set; }

/// <summary>
/// Title of the issue
/// </summary>
Expand Down Expand Up @@ -186,11 +192,13 @@ public IssueUpdate ToUpdate()
: Labels.Select(x => x.Name);

ItemState state;
ItemStateReason stateReason;
var issueUpdate = new IssueUpdate
{
Body = Body,
Milestone = milestoneId,
State = (State.TryParse(out state) ? (ItemState?)state : null),
StateReason = (StateReason.HasValue && StateReason.Value.TryParse(out stateReason) ? (ItemStateReason?)stateReason : null),
Title = Title
};

Expand Down