Skip to content

Commit

Permalink
Add API to manage issue dependencies (#17935)
Browse files Browse the repository at this point in the history
Adds API endpoints to manage issue/PR dependencies
* `GET /repos/{owner}/{repo}/issues/{index}/blocks` List issues that are
blocked by this issue
* `POST /repos/{owner}/{repo}/issues/{index}/blocks` Block the issue
given in the body by the issue in path
* `DELETE /repos/{owner}/{repo}/issues/{index}/blocks` Unblock the issue
given in the body by the issue in path
* `GET /repos/{owner}/{repo}/issues/{index}/dependencies` List an
issue's dependencies
* `POST /repos/{owner}/{repo}/issues/{index}/dependencies` Create a new
issue dependencies
* `DELETE /repos/{owner}/{repo}/issues/{index}/dependencies` Remove an
issue dependency

Closes #15393
Closes #22115

Co-authored-by: Andrew Thornton <art27@cantab.net>
  • Loading branch information
qwerty287 and zeripath authored Mar 28, 2023
1 parent 85e8c83 commit 3cab9c6
Show file tree
Hide file tree
Showing 12 changed files with 1,074 additions and 34 deletions.
2 changes: 1 addition & 1 deletion models/issues/dependency.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ func CreateIssueDependency(user *user_model.User, issue, dep *Issue) error {
}
defer committer.Close()

// Check if it aleready exists
// Check if it already exists
exists, err := issueDepExists(ctx, issue.ID, dep.ID)
if err != nil {
return err
Expand Down
23 changes: 14 additions & 9 deletions models/issues/issue.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ func (issue *Issue) IsOverdue() bool {

// LoadRepo loads issue's repository
func (issue *Issue) LoadRepo(ctx context.Context) (err error) {
if issue.Repo == nil {
if issue.Repo == nil && issue.RepoID != 0 {
issue.Repo, err = repo_model.GetRepositoryByID(ctx, issue.RepoID)
if err != nil {
return fmt.Errorf("getRepositoryByID [%d]: %w", issue.RepoID, err)
Expand Down Expand Up @@ -223,7 +223,7 @@ func (issue *Issue) GetPullRequest() (pr *PullRequest, err error) {

// LoadLabels loads labels
func (issue *Issue) LoadLabels(ctx context.Context) (err error) {
if issue.Labels == nil {
if issue.Labels == nil && issue.ID != 0 {
issue.Labels, err = GetLabelsByIssueID(ctx, issue.ID)
if err != nil {
return fmt.Errorf("getLabelsByIssueID [%d]: %w", issue.ID, err)
Expand All @@ -234,7 +234,7 @@ func (issue *Issue) LoadLabels(ctx context.Context) (err error) {

// LoadPoster loads poster
func (issue *Issue) LoadPoster(ctx context.Context) (err error) {
if issue.Poster == nil {
if issue.Poster == nil && issue.PosterID != 0 {
issue.Poster, err = user_model.GetPossibleUserByID(ctx, issue.PosterID)
if err != nil {
issue.PosterID = -1
Expand All @@ -252,7 +252,7 @@ func (issue *Issue) LoadPoster(ctx context.Context) (err error) {
// LoadPullRequest loads pull request info
func (issue *Issue) LoadPullRequest(ctx context.Context) (err error) {
if issue.IsPull {
if issue.PullRequest == nil {
if issue.PullRequest == nil && issue.ID != 0 {
issue.PullRequest, err = GetPullRequestByIssueID(ctx, issue.ID)
if err != nil {
if IsErrPullRequestNotExist(err) {
Expand All @@ -261,7 +261,9 @@ func (issue *Issue) LoadPullRequest(ctx context.Context) (err error) {
return fmt.Errorf("getPullRequestByIssueID [%d]: %w", issue.ID, err)
}
}
issue.PullRequest.Issue = issue
if issue.PullRequest != nil {
issue.PullRequest.Issue = issue
}
}
return nil
}
Expand Down Expand Up @@ -2128,15 +2130,18 @@ func (issue *Issue) GetParticipantIDsByIssue(ctx context.Context) ([]int64, erro
}

// BlockedByDependencies finds all Dependencies an issue is blocked by
func (issue *Issue) BlockedByDependencies(ctx context.Context) (issueDeps []*DependencyInfo, err error) {
err = db.GetEngine(ctx).
func (issue *Issue) BlockedByDependencies(ctx context.Context, opts db.ListOptions) (issueDeps []*DependencyInfo, err error) {
sess := db.GetEngine(ctx).
Table("issue").
Join("INNER", "repository", "repository.id = issue.repo_id").
Join("INNER", "issue_dependency", "issue_dependency.dependency_id = issue.id").
Where("issue_id = ?", issue.ID).
// sort by repo id then created date, with the issues of the same repo at the beginning of the list
OrderBy("CASE WHEN issue.repo_id = ? THEN 0 ELSE issue.repo_id END, issue.created_unix DESC", issue.RepoID).
Find(&issueDeps)
OrderBy("CASE WHEN issue.repo_id = ? THEN 0 ELSE issue.repo_id END, issue.created_unix DESC", issue.RepoID)
if opts.Page != 0 {
sess = db.SetSessionPagination(sess, &opts)
}
err = sess.Find(&issueDeps)

for _, depInfo := range issueDeps {
depInfo.Issue.Repo = &depInfo.Repository
Expand Down
8 changes: 8 additions & 0 deletions modules/structs/issue.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,3 +211,11 @@ func (it IssueTemplate) Type() IssueTemplateType {
}
return ""
}

// IssueMeta basic issue information
// swagger:model
type IssueMeta struct {
Index int64 `json:"index"`
Owner string `json:"owner"`
Name string `json:"repo"`
}
3 changes: 3 additions & 0 deletions options/locale/locale_en-US.ini
Original file line number Diff line number Diff line change
Expand Up @@ -1489,6 +1489,9 @@ issues.due_date_invalid = "The due date is invalid or out of range. Please use t
issues.dependency.title = Dependencies
issues.dependency.issue_no_dependencies = No dependencies set.
issues.dependency.pr_no_dependencies = No dependencies set.
issues.dependency.no_permission_1 = "You do not have permission to read %d dependency"
issues.dependency.no_permission_n = "You do not have permission to read %d dependencies"
issues.dependency.no_permission.can_remove = "You do not have permission to read this dependency but can remove this dependency"
issues.dependency.add = Add dependency…
issues.dependency.cancel = Cancel
issues.dependency.remove = Remove
Expand Down
8 changes: 8 additions & 0 deletions routers/api/v1/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -1026,6 +1026,14 @@ func Routes(ctx gocontext.Context) *web.Route {
Patch(reqToken(auth_model.AccessTokenScopeRepo), mustNotBeArchived, bind(api.EditAttachmentOptions{}), repo.EditIssueAttachment).
Delete(reqToken(auth_model.AccessTokenScopeRepo), mustNotBeArchived, repo.DeleteIssueAttachment)
}, mustEnableAttachments)
m.Combo("/dependencies").
Get(repo.GetIssueDependencies).
Post(reqToken(auth_model.AccessTokenScopeRepo), mustNotBeArchived, bind(api.IssueMeta{}), repo.CreateIssueDependency).
Delete(reqToken(auth_model.AccessTokenScopeRepo), mustNotBeArchived, bind(api.IssueMeta{}), repo.RemoveIssueDependency)
m.Combo("/blocks").
Get(repo.GetIssueBlocks).
Post(reqToken(auth_model.AccessTokenScopeRepo), bind(api.IssueMeta{}), repo.CreateIssueBlocking).
Delete(reqToken(auth_model.AccessTokenScopeRepo), bind(api.IssueMeta{}), repo.RemoveIssueBlocking)
})
}, mustEnableIssuesOrPulls)
m.Group("/labels", func() {
Expand Down
Loading

0 comments on commit 3cab9c6

Please sign in to comment.