You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

issue_dependency.go 4.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. // Copyright 2018 The Gitea Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package models
  5. import (
  6. "code.gitea.io/gitea/models/db"
  7. "code.gitea.io/gitea/models/unit"
  8. user_model "code.gitea.io/gitea/models/user"
  9. "code.gitea.io/gitea/modules/log"
  10. "code.gitea.io/gitea/modules/setting"
  11. "code.gitea.io/gitea/modules/timeutil"
  12. )
  13. // IssueDependency represents an issue dependency
  14. type IssueDependency struct {
  15. ID int64 `xorm:"pk autoincr"`
  16. UserID int64 `xorm:"NOT NULL"`
  17. IssueID int64 `xorm:"UNIQUE(issue_dependency) NOT NULL"`
  18. DependencyID int64 `xorm:"UNIQUE(issue_dependency) NOT NULL"`
  19. CreatedUnix timeutil.TimeStamp `xorm:"created"`
  20. UpdatedUnix timeutil.TimeStamp `xorm:"updated"`
  21. }
  22. func init() {
  23. db.RegisterModel(new(IssueDependency))
  24. }
  25. // DependencyType Defines Dependency Type Constants
  26. type DependencyType int
  27. // Define Dependency Types
  28. const (
  29. DependencyTypeBlockedBy DependencyType = iota
  30. DependencyTypeBlocking
  31. )
  32. // CreateIssueDependency creates a new dependency for an issue
  33. func CreateIssueDependency(user *user_model.User, issue, dep *Issue) error {
  34. ctx, committer, err := db.TxContext()
  35. if err != nil {
  36. return err
  37. }
  38. defer committer.Close()
  39. sess := db.GetEngine(ctx)
  40. // Check if it aleready exists
  41. exists, err := issueDepExists(sess, issue.ID, dep.ID)
  42. if err != nil {
  43. return err
  44. }
  45. if exists {
  46. return ErrDependencyExists{issue.ID, dep.ID}
  47. }
  48. // And if it would be circular
  49. circular, err := issueDepExists(sess, dep.ID, issue.ID)
  50. if err != nil {
  51. return err
  52. }
  53. if circular {
  54. return ErrCircularDependency{issue.ID, dep.ID}
  55. }
  56. if err := db.Insert(ctx, &IssueDependency{
  57. UserID: user.ID,
  58. IssueID: issue.ID,
  59. DependencyID: dep.ID,
  60. }); err != nil {
  61. return err
  62. }
  63. // Add comment referencing the new dependency
  64. if err = createIssueDependencyComment(ctx, user, issue, dep, true); err != nil {
  65. return err
  66. }
  67. return committer.Commit()
  68. }
  69. // RemoveIssueDependency removes a dependency from an issue
  70. func RemoveIssueDependency(user *user_model.User, issue, dep *Issue, depType DependencyType) (err error) {
  71. ctx, committer, err := db.TxContext()
  72. if err != nil {
  73. return err
  74. }
  75. defer committer.Close()
  76. var issueDepToDelete IssueDependency
  77. switch depType {
  78. case DependencyTypeBlockedBy:
  79. issueDepToDelete = IssueDependency{IssueID: issue.ID, DependencyID: dep.ID}
  80. case DependencyTypeBlocking:
  81. issueDepToDelete = IssueDependency{IssueID: dep.ID, DependencyID: issue.ID}
  82. default:
  83. return ErrUnknownDependencyType{depType}
  84. }
  85. affected, err := db.GetEngine(ctx).Delete(&issueDepToDelete)
  86. if err != nil {
  87. return err
  88. }
  89. // If we deleted nothing, the dependency did not exist
  90. if affected <= 0 {
  91. return ErrDependencyNotExists{issue.ID, dep.ID}
  92. }
  93. // Add comment referencing the removed dependency
  94. if err = createIssueDependencyComment(ctx, user, issue, dep, false); err != nil {
  95. return err
  96. }
  97. return committer.Commit()
  98. }
  99. // Check if the dependency already exists
  100. func issueDepExists(e db.Engine, issueID, depID int64) (bool, error) {
  101. return e.Where("(issue_id = ? AND dependency_id = ?)", issueID, depID).Exist(&IssueDependency{})
  102. }
  103. // IssueNoDependenciesLeft checks if issue can be closed
  104. func IssueNoDependenciesLeft(issue *Issue) (bool, error) {
  105. return issueNoDependenciesLeft(db.GetEngine(db.DefaultContext), issue)
  106. }
  107. func issueNoDependenciesLeft(e db.Engine, issue *Issue) (bool, error) {
  108. exists, err := e.
  109. Table("issue_dependency").
  110. Select("issue.*").
  111. Join("INNER", "issue", "issue.id = issue_dependency.dependency_id").
  112. Where("issue_dependency.issue_id = ?", issue.ID).
  113. And("issue.is_closed = ?", "0").
  114. Exist(&Issue{})
  115. return !exists, err
  116. }
  117. // IsDependenciesEnabled returns if dependencies are enabled and returns the default setting if not set.
  118. func (repo *Repository) IsDependenciesEnabled() bool {
  119. return repo.isDependenciesEnabled(db.GetEngine(db.DefaultContext))
  120. }
  121. func (repo *Repository) isDependenciesEnabled(e db.Engine) bool {
  122. var u *RepoUnit
  123. var err error
  124. if u, err = repo.getUnit(e, unit.TypeIssues); err != nil {
  125. log.Trace("%s", err)
  126. return setting.Service.DefaultEnableDependencies
  127. }
  128. return u.IssuesConfig().EnableDependencies
  129. }