diff options
Diffstat (limited to 'models/issues')
-rw-r--r-- | models/issues/comment.go | 211 | ||||
-rw-r--r-- | models/issues/dependency.go | 123 | ||||
-rw-r--r-- | models/issues/issue.go | 18 | ||||
-rw-r--r-- | models/issues/issue_index.go | 22 | ||||
-rw-r--r-- | models/issues/issue_label.go | 206 | ||||
-rw-r--r-- | models/issues/issue_lock.go | 33 | ||||
-rw-r--r-- | models/issues/issue_update.go | 247 | ||||
-rw-r--r-- | models/issues/label.go | 71 | ||||
-rw-r--r-- | models/issues/milestone.go | 176 | ||||
-rw-r--r-- | models/issues/pull.go | 98 | ||||
-rw-r--r-- | models/issues/reaction.go | 18 | ||||
-rw-r--r-- | models/issues/review.go | 572 | ||||
-rw-r--r-- | models/issues/tracked_time.go | 146 |
13 files changed, 833 insertions, 1108 deletions
diff --git a/models/issues/comment.go b/models/issues/comment.go index 4fdb0c1808..d22f08fa87 100644 --- a/models/issues/comment.go +++ b/models/issues/comment.go @@ -766,81 +766,73 @@ func (c *Comment) CodeCommentLink(ctx context.Context) string { // CreateComment creates comment with context func CreateComment(ctx context.Context, opts *CreateCommentOptions) (_ *Comment, err error) { - ctx, committer, err := db.TxContext(ctx) - if err != nil { - return nil, err - } - defer committer.Close() - - e := db.GetEngine(ctx) - var LabelID int64 - if opts.Label != nil { - LabelID = opts.Label.ID - } + return db.WithTx2(ctx, func(ctx context.Context) (*Comment, error) { + var LabelID int64 + if opts.Label != nil { + LabelID = opts.Label.ID + } - var commentMetaData *CommentMetaData - if opts.ProjectColumnTitle != "" { - commentMetaData = &CommentMetaData{ - ProjectColumnID: opts.ProjectColumnID, - ProjectColumnTitle: opts.ProjectColumnTitle, - ProjectTitle: opts.ProjectTitle, + var commentMetaData *CommentMetaData + if opts.ProjectColumnTitle != "" { + commentMetaData = &CommentMetaData{ + ProjectColumnID: opts.ProjectColumnID, + ProjectColumnTitle: opts.ProjectColumnTitle, + ProjectTitle: opts.ProjectTitle, + } } - } - comment := &Comment{ - Type: opts.Type, - PosterID: opts.Doer.ID, - Poster: opts.Doer, - IssueID: opts.Issue.ID, - LabelID: LabelID, - OldMilestoneID: opts.OldMilestoneID, - MilestoneID: opts.MilestoneID, - OldProjectID: opts.OldProjectID, - ProjectID: opts.ProjectID, - TimeID: opts.TimeID, - RemovedAssignee: opts.RemovedAssignee, - AssigneeID: opts.AssigneeID, - AssigneeTeamID: opts.AssigneeTeamID, - CommitID: opts.CommitID, - CommitSHA: opts.CommitSHA, - Line: opts.LineNum, - Content: opts.Content, - OldTitle: opts.OldTitle, - NewTitle: opts.NewTitle, - OldRef: opts.OldRef, - NewRef: opts.NewRef, - DependentIssueID: opts.DependentIssueID, - TreePath: opts.TreePath, - ReviewID: opts.ReviewID, - Patch: opts.Patch, - RefRepoID: opts.RefRepoID, - RefIssueID: opts.RefIssueID, - RefCommentID: opts.RefCommentID, - RefAction: opts.RefAction, - RefIsPull: opts.RefIsPull, - IsForcePush: opts.IsForcePush, - Invalidated: opts.Invalidated, - CommentMetaData: commentMetaData, - } - if _, err = e.Insert(comment); err != nil { - return nil, err - } + comment := &Comment{ + Type: opts.Type, + PosterID: opts.Doer.ID, + Poster: opts.Doer, + IssueID: opts.Issue.ID, + LabelID: LabelID, + OldMilestoneID: opts.OldMilestoneID, + MilestoneID: opts.MilestoneID, + OldProjectID: opts.OldProjectID, + ProjectID: opts.ProjectID, + TimeID: opts.TimeID, + RemovedAssignee: opts.RemovedAssignee, + AssigneeID: opts.AssigneeID, + AssigneeTeamID: opts.AssigneeTeamID, + CommitID: opts.CommitID, + CommitSHA: opts.CommitSHA, + Line: opts.LineNum, + Content: opts.Content, + OldTitle: opts.OldTitle, + NewTitle: opts.NewTitle, + OldRef: opts.OldRef, + NewRef: opts.NewRef, + DependentIssueID: opts.DependentIssueID, + TreePath: opts.TreePath, + ReviewID: opts.ReviewID, + Patch: opts.Patch, + RefRepoID: opts.RefRepoID, + RefIssueID: opts.RefIssueID, + RefCommentID: opts.RefCommentID, + RefAction: opts.RefAction, + RefIsPull: opts.RefIsPull, + IsForcePush: opts.IsForcePush, + Invalidated: opts.Invalidated, + CommentMetaData: commentMetaData, + } + if err = db.Insert(ctx, comment); err != nil { + return nil, err + } - if err = opts.Repo.LoadOwner(ctx); err != nil { - return nil, err - } + if err = opts.Repo.LoadOwner(ctx); err != nil { + return nil, err + } - if err = updateCommentInfos(ctx, opts, comment); err != nil { - return nil, err - } + if err = updateCommentInfos(ctx, opts, comment); err != nil { + return nil, err + } - if err = comment.AddCrossReferences(ctx, opts.Doer, false); err != nil { - return nil, err - } - if err = committer.Commit(); err != nil { - return nil, err - } - return comment, nil + if err = comment.AddCrossReferences(ctx, opts.Doer, false); err != nil { + return nil, err + } + return comment, nil + }) } func updateCommentInfos(ctx context.Context, opts *CreateCommentOptions, comment *Comment) (err error) { @@ -1092,33 +1084,21 @@ func UpdateCommentInvalidate(ctx context.Context, c *Comment) error { // UpdateComment updates information of comment. func UpdateComment(ctx context.Context, c *Comment, contentVersion int, doer *user_model.User) error { - ctx, committer, err := db.TxContext(ctx) - if err != nil { - return err - } - defer committer.Close() - sess := db.GetEngine(ctx) - - c.ContentVersion = contentVersion + 1 - - affected, err := sess.ID(c.ID).AllCols().Where("content_version = ?", contentVersion).Update(c) - if err != nil { - return err - } - if affected == 0 { - return ErrCommentAlreadyChanged - } - if err := c.LoadIssue(ctx); err != nil { - return err - } - if err := c.AddCrossReferences(ctx, doer, true); err != nil { - return err - } - if err := committer.Commit(); err != nil { - return fmt.Errorf("Commit: %w", err) - } + return db.WithTx(ctx, func(ctx context.Context) error { + c.ContentVersion = contentVersion + 1 - return nil + affected, err := db.GetEngine(ctx).ID(c.ID).AllCols().Where("content_version = ?", contentVersion).Update(c) + if err != nil { + return err + } + if affected == 0 { + return ErrCommentAlreadyChanged + } + if err := c.LoadIssue(ctx); err != nil { + return err + } + return c.AddCrossReferences(ctx, doer, true) + }) } // DeleteComment deletes the comment @@ -1277,31 +1257,28 @@ func InsertIssueComments(ctx context.Context, comments []*Comment) error { return comment.IssueID, true }) - ctx, committer, err := db.TxContext(ctx) - if err != nil { - return err - } - defer committer.Close() - for _, comment := range comments { - if _, err := db.GetEngine(ctx).NoAutoTime().Insert(comment); err != nil { - return err - } - - for _, reaction := range comment.Reactions { - reaction.IssueID = comment.IssueID - reaction.CommentID = comment.ID - } - if len(comment.Reactions) > 0 { - if err := db.Insert(ctx, comment.Reactions); err != nil { + return db.WithTx(ctx, func(ctx context.Context) error { + for _, comment := range comments { + if _, err := db.GetEngine(ctx).NoAutoTime().Insert(comment); err != nil { return err } + + for _, reaction := range comment.Reactions { + reaction.IssueID = comment.IssueID + reaction.CommentID = comment.ID + } + if len(comment.Reactions) > 0 { + if err := db.Insert(ctx, comment.Reactions); err != nil { + return err + } + } } - } - for _, issueID := range issueIDs { - if err := UpdateIssueNumComments(ctx, issueID); err != nil { - return err + for _, issueID := range issueIDs { + if err := UpdateIssueNumComments(ctx, issueID); err != nil { + return err + } } - } - return committer.Commit() + return nil + }) } diff --git a/models/issues/dependency.go b/models/issues/dependency.go index 146dd1887d..0eaa47e359 100644 --- a/models/issues/dependency.go +++ b/models/issues/dependency.go @@ -128,79 +128,64 @@ const ( // CreateIssueDependency creates a new dependency for an issue func CreateIssueDependency(ctx context.Context, user *user_model.User, issue, dep *Issue) error { - ctx, committer, err := db.TxContext(ctx) - if err != nil { - return err - } - defer committer.Close() - - // Check if it already exists - exists, err := issueDepExists(ctx, issue.ID, dep.ID) - if err != nil { - return err - } - if exists { - return ErrDependencyExists{issue.ID, dep.ID} - } - // And if it would be circular - circular, err := issueDepExists(ctx, dep.ID, issue.ID) - if err != nil { - return err - } - if circular { - return ErrCircularDependency{issue.ID, dep.ID} - } - - if err := db.Insert(ctx, &IssueDependency{ - UserID: user.ID, - IssueID: issue.ID, - DependencyID: dep.ID, - }); err != nil { - return err - } - - // Add comment referencing the new dependency - if err = createIssueDependencyComment(ctx, user, issue, dep, true); err != nil { - return err - } - - return committer.Commit() + return db.WithTx(ctx, func(ctx context.Context) error { + // Check if it already exists + exists, err := issueDepExists(ctx, issue.ID, dep.ID) + if err != nil { + return err + } + if exists { + return ErrDependencyExists{issue.ID, dep.ID} + } + // And if it would be circular + circular, err := issueDepExists(ctx, dep.ID, issue.ID) + if err != nil { + return err + } + if circular { + return ErrCircularDependency{issue.ID, dep.ID} + } + + if err := db.Insert(ctx, &IssueDependency{ + UserID: user.ID, + IssueID: issue.ID, + DependencyID: dep.ID, + }); err != nil { + return err + } + + // Add comment referencing the new dependency + return createIssueDependencyComment(ctx, user, issue, dep, true) + }) } // RemoveIssueDependency removes a dependency from an issue func RemoveIssueDependency(ctx context.Context, user *user_model.User, issue, dep *Issue, depType DependencyType) (err error) { - ctx, committer, err := db.TxContext(ctx) - if err != nil { - return err - } - defer committer.Close() - - var issueDepToDelete IssueDependency - - switch depType { - case DependencyTypeBlockedBy: - issueDepToDelete = IssueDependency{IssueID: issue.ID, DependencyID: dep.ID} - case DependencyTypeBlocking: - issueDepToDelete = IssueDependency{IssueID: dep.ID, DependencyID: issue.ID} - default: - return ErrUnknownDependencyType{depType} - } - - affected, err := db.GetEngine(ctx).Delete(&issueDepToDelete) - if err != nil { - return err - } - - // If we deleted nothing, the dependency did not exist - if affected <= 0 { - return ErrDependencyNotExists{issue.ID, dep.ID} - } - - // Add comment referencing the removed dependency - if err = createIssueDependencyComment(ctx, user, issue, dep, false); err != nil { - return err - } - return committer.Commit() + return db.WithTx(ctx, func(ctx context.Context) error { + var issueDepToDelete IssueDependency + + switch depType { + case DependencyTypeBlockedBy: + issueDepToDelete = IssueDependency{IssueID: issue.ID, DependencyID: dep.ID} + case DependencyTypeBlocking: + issueDepToDelete = IssueDependency{IssueID: dep.ID, DependencyID: issue.ID} + default: + return ErrUnknownDependencyType{depType} + } + + affected, err := db.GetEngine(ctx).Delete(&issueDepToDelete) + if err != nil { + return err + } + + // If we deleted nothing, the dependency did not exist + if affected <= 0 { + return ErrDependencyNotExists{issue.ID, dep.ID} + } + + // Add comment referencing the removed dependency + return createIssueDependencyComment(ctx, user, issue, dep, false) + }) } // Check if the dependency already exists diff --git a/models/issues/issue.go b/models/issues/issue.go index a86d50ca9d..ef651359ab 100644 --- a/models/issues/issue.go +++ b/models/issues/issue.go @@ -755,18 +755,14 @@ func (issue *Issue) HasOriginalAuthor() bool { // InsertIssues insert issues to database func InsertIssues(ctx context.Context, issues ...*Issue) error { - ctx, committer, err := db.TxContext(ctx) - if err != nil { - return err - } - defer committer.Close() - - for _, issue := range issues { - if err := insertIssue(ctx, issue); err != nil { - return err + return db.WithTx(ctx, func(ctx context.Context) error { + for _, issue := range issues { + if err := insertIssue(ctx, issue); err != nil { + return err + } } - } - return committer.Commit() + return nil + }) } func insertIssue(ctx context.Context, issue *Issue) error { diff --git a/models/issues/issue_index.go b/models/issues/issue_index.go index 2eb61858bf..1fe4a08a09 100644 --- a/models/issues/issue_index.go +++ b/models/issues/issue_index.go @@ -12,20 +12,12 @@ import ( // RecalculateIssueIndexForRepo create issue_index for repo if not exist and // update it based on highest index of existing issues assigned to a repo func RecalculateIssueIndexForRepo(ctx context.Context, repoID int64) error { - ctx, committer, err := db.TxContext(ctx) - if err != nil { - return err - } - defer committer.Close() + return db.WithTx(ctx, func(ctx context.Context) error { + var maxIndex int64 + if _, err := db.GetEngine(ctx).Select(" MAX(`index`)").Table("issue").Where("repo_id=?", repoID).Get(&maxIndex); err != nil { + return err + } - var maxIndex int64 - if _, err = db.GetEngine(ctx).Select(" MAX(`index`)").Table("issue").Where("repo_id=?", repoID).Get(&maxIndex); err != nil { - return err - } - - if err = db.SyncMaxResourceIndex(ctx, "issue_index", repoID, maxIndex); err != nil { - return err - } - - return committer.Commit() + return db.SyncMaxResourceIndex(ctx, "issue_index", repoID, maxIndex) + }) } diff --git a/models/issues/issue_label.go b/models/issues/issue_label.go index f082079e07..151469a9b8 100644 --- a/models/issues/issue_label.go +++ b/models/issues/issue_label.go @@ -88,36 +88,28 @@ func NewIssueLabel(ctx context.Context, issue *Issue, label *Label, doer *user_m return nil } - ctx, committer, err := db.TxContext(ctx) - if err != nil { - return err - } - defer committer.Close() - - if err = issue.LoadRepo(ctx); err != nil { - return err - } - - // Do NOT add invalid labels - if issue.RepoID != label.RepoID && issue.Repo.OwnerID != label.OrgID { - return nil - } + return db.WithTx(ctx, func(ctx context.Context) error { + if err = issue.LoadRepo(ctx); err != nil { + return err + } - if err = RemoveDuplicateExclusiveIssueLabels(ctx, issue, label, doer); err != nil { - return nil - } + // Do NOT add invalid labels + if issue.RepoID != label.RepoID && issue.Repo.OwnerID != label.OrgID { + return nil + } - if err = newIssueLabel(ctx, issue, label, doer); err != nil { - return err - } + if err = RemoveDuplicateExclusiveIssueLabels(ctx, issue, label, doer); err != nil { + return nil + } - issue.isLabelsLoaded = false - issue.Labels = nil - if err = issue.LoadLabels(ctx); err != nil { - return err - } + if err = newIssueLabel(ctx, issue, label, doer); err != nil { + return err + } - return committer.Commit() + issue.isLabelsLoaded = false + issue.Labels = nil + return issue.LoadLabels(ctx) + }) } // newIssueLabels add labels to an issue. It will check if the labels are valid for the issue @@ -151,24 +143,16 @@ func newIssueLabels(ctx context.Context, issue *Issue, labels []*Label, doer *us // NewIssueLabels creates a list of issue-label relations. func NewIssueLabels(ctx context.Context, issue *Issue, labels []*Label, doer *user_model.User) (err error) { - ctx, committer, err := db.TxContext(ctx) - if err != nil { - return err - } - defer committer.Close() - - if err = newIssueLabels(ctx, issue, labels, doer); err != nil { - return err - } - - // reload all labels - issue.isLabelsLoaded = false - issue.Labels = nil - if err = issue.LoadLabels(ctx); err != nil { - return err - } + return db.WithTx(ctx, func(ctx context.Context) error { + if err = newIssueLabels(ctx, issue, labels, doer); err != nil { + return err + } - return committer.Commit() + // reload all labels + issue.isLabelsLoaded = false + issue.Labels = nil + return issue.LoadLabels(ctx) + }) } func deleteIssueLabel(ctx context.Context, issue *Issue, label *Label, doer *user_model.User) (err error) { @@ -365,35 +349,23 @@ func clearIssueLabels(ctx context.Context, issue *Issue, doer *user_model.User) // ClearIssueLabels removes all issue labels as the given user. // Triggers appropriate WebHooks, if any. func ClearIssueLabels(ctx context.Context, issue *Issue, doer *user_model.User) (err error) { - ctx, committer, err := db.TxContext(ctx) - if err != nil { - return err - } - defer committer.Close() - - if err := issue.LoadRepo(ctx); err != nil { - return err - } else if err = issue.LoadPullRequest(ctx); err != nil { - return err - } - - perm, err := access_model.GetUserRepoPermission(ctx, issue.Repo, doer) - if err != nil { - return err - } - if !perm.CanWriteIssuesOrPulls(issue.IsPull) { - return ErrRepoLabelNotExist{} - } - - if err = clearIssueLabels(ctx, issue, doer); err != nil { - return err - } + return db.WithTx(ctx, func(ctx context.Context) error { + if err := issue.LoadRepo(ctx); err != nil { + return err + } else if err = issue.LoadPullRequest(ctx); err != nil { + return err + } - if err = committer.Commit(); err != nil { - return fmt.Errorf("Commit: %w", err) - } + perm, err := access_model.GetUserRepoPermission(ctx, issue.Repo, doer) + if err != nil { + return err + } + if !perm.CanWriteIssuesOrPulls(issue.IsPull) { + return ErrRepoLabelNotExist{} + } - return nil + return clearIssueLabels(ctx, issue, doer) + }) } type labelSorter []*Label @@ -438,69 +410,61 @@ func RemoveDuplicateExclusiveLabels(labels []*Label) []*Label { // ReplaceIssueLabels removes all current labels and add new labels to the issue. // Triggers appropriate WebHooks, if any. func ReplaceIssueLabels(ctx context.Context, issue *Issue, labels []*Label, doer *user_model.User) (err error) { - ctx, committer, err := db.TxContext(ctx) - if err != nil { - return err - } - defer committer.Close() + return db.WithTx(ctx, func(ctx context.Context) error { + if err = issue.LoadRepo(ctx); err != nil { + return err + } - if err = issue.LoadRepo(ctx); err != nil { - return err - } + if err = issue.LoadLabels(ctx); err != nil { + return err + } - if err = issue.LoadLabels(ctx); err != nil { - return err - } + labels = RemoveDuplicateExclusiveLabels(labels) - labels = RemoveDuplicateExclusiveLabels(labels) + sort.Sort(labelSorter(labels)) + sort.Sort(labelSorter(issue.Labels)) - sort.Sort(labelSorter(labels)) - sort.Sort(labelSorter(issue.Labels)) + var toAdd, toRemove []*Label - var toAdd, toRemove []*Label + addIndex, removeIndex := 0, 0 + for addIndex < len(labels) && removeIndex < len(issue.Labels) { + addLabel := labels[addIndex] + removeLabel := issue.Labels[removeIndex] + if addLabel.ID == removeLabel.ID { + // Silently drop invalid labels + if removeLabel.RepoID != issue.RepoID && removeLabel.OrgID != issue.Repo.OwnerID { + toRemove = append(toRemove, removeLabel) + } - addIndex, removeIndex := 0, 0 - for addIndex < len(labels) && removeIndex < len(issue.Labels) { - addLabel := labels[addIndex] - removeLabel := issue.Labels[removeIndex] - if addLabel.ID == removeLabel.ID { - // Silently drop invalid labels - if removeLabel.RepoID != issue.RepoID && removeLabel.OrgID != issue.Repo.OwnerID { + addIndex++ + removeIndex++ + } else if addLabel.ID < removeLabel.ID { + // Only add if the label is valid + if addLabel.RepoID == issue.RepoID || addLabel.OrgID == issue.Repo.OwnerID { + toAdd = append(toAdd, addLabel) + } + addIndex++ + } else { toRemove = append(toRemove, removeLabel) + removeIndex++ } - - addIndex++ - removeIndex++ - } else if addLabel.ID < removeLabel.ID { - // Only add if the label is valid - if addLabel.RepoID == issue.RepoID || addLabel.OrgID == issue.Repo.OwnerID { - toAdd = append(toAdd, addLabel) - } - addIndex++ - } else { - toRemove = append(toRemove, removeLabel) - removeIndex++ } - } - toAdd = append(toAdd, labels[addIndex:]...) - toRemove = append(toRemove, issue.Labels[removeIndex:]...) + toAdd = append(toAdd, labels[addIndex:]...) + toRemove = append(toRemove, issue.Labels[removeIndex:]...) - if len(toAdd) > 0 { - if err = newIssueLabels(ctx, issue, toAdd, doer); err != nil { - return fmt.Errorf("addLabels: %w", err) + if len(toAdd) > 0 { + if err = newIssueLabels(ctx, issue, toAdd, doer); err != nil { + return fmt.Errorf("addLabels: %w", err) + } } - } - for _, l := range toRemove { - if err = deleteIssueLabel(ctx, issue, l, doer); err != nil { - return fmt.Errorf("removeLabel: %w", err) + for _, l := range toRemove { + if err = deleteIssueLabel(ctx, issue, l, doer); err != nil { + return fmt.Errorf("removeLabel: %w", err) + } } - } - - issue.Labels = nil - if err = issue.LoadLabels(ctx); err != nil { - return err - } - return committer.Commit() + issue.Labels = nil + return issue.LoadLabels(ctx) + }) } diff --git a/models/issues/issue_lock.go b/models/issues/issue_lock.go index fa0d128f74..2e5bf64cc6 100644 --- a/models/issues/issue_lock.go +++ b/models/issues/issue_lock.go @@ -47,26 +47,19 @@ func updateIssueLock(ctx context.Context, opts *IssueLockOptions, lock bool) err commentType = CommentTypeUnlock } - ctx, committer, err := db.TxContext(ctx) - if err != nil { - return err - } - defer committer.Close() - - if err := UpdateIssueCols(ctx, opts.Issue, "is_locked"); err != nil { - return err - } + return db.WithTx(ctx, func(ctx context.Context) error { + if err := UpdateIssueCols(ctx, opts.Issue, "is_locked"); err != nil { + return err + } - opt := &CreateCommentOptions{ - Doer: opts.Doer, - Issue: opts.Issue, - Repo: opts.Issue.Repo, - Type: commentType, - Content: opts.Reason, - } - if _, err := CreateComment(ctx, opt); err != nil { + opt := &CreateCommentOptions{ + Doer: opts.Doer, + Issue: opts.Issue, + Repo: opts.Issue.Repo, + Type: commentType, + Content: opts.Reason, + } + _, err := CreateComment(ctx, opt) return err - } - - return committer.Commit() + }) } diff --git a/models/issues/issue_update.go b/models/issues/issue_update.go index 9b99787e3b..1c16817491 100644 --- a/models/issues/issue_update.go +++ b/models/issues/issue_update.go @@ -167,20 +167,9 @@ func CloseIssue(ctx context.Context, issue *Issue, doer *user_model.User) (*Comm return nil, err } - ctx, committer, err := db.TxContext(ctx) - if err != nil { - return nil, err - } - defer committer.Close() - - comment, err := SetIssueAsClosed(ctx, issue, doer, false) - if err != nil { - return nil, err - } - if err := committer.Commit(); err != nil { - return nil, err - } - return comment, nil + return db.WithTx2(ctx, func(ctx context.Context) (*Comment, error) { + return SetIssueAsClosed(ctx, issue, doer, false) + }) } // ReopenIssue changes issue status to open. @@ -192,88 +181,64 @@ func ReopenIssue(ctx context.Context, issue *Issue, doer *user_model.User) (*Com return nil, err } - ctx, committer, err := db.TxContext(ctx) - if err != nil { - return nil, err - } - defer committer.Close() - - comment, err := setIssueAsReopen(ctx, issue, doer) - if err != nil { - return nil, err - } - if err := committer.Commit(); err != nil { - return nil, err - } - return comment, nil + return db.WithTx2(ctx, func(ctx context.Context) (*Comment, error) { + return setIssueAsReopen(ctx, issue, doer) + }) } // ChangeIssueTitle changes the title of this issue, as the given user. func ChangeIssueTitle(ctx context.Context, issue *Issue, doer *user_model.User, oldTitle string) (err error) { - ctx, committer, err := db.TxContext(ctx) - if err != nil { - return err - } - defer committer.Close() - - issue.Title = util.EllipsisDisplayString(issue.Title, 255) - if err = UpdateIssueCols(ctx, issue, "name"); err != nil { - return fmt.Errorf("updateIssueCols: %w", err) - } - - if err = issue.LoadRepo(ctx); err != nil { - return fmt.Errorf("loadRepo: %w", err) - } + return db.WithTx(ctx, func(ctx context.Context) error { + issue.Title = util.EllipsisDisplayString(issue.Title, 255) + if err = UpdateIssueCols(ctx, issue, "name"); err != nil { + return fmt.Errorf("updateIssueCols: %w", err) + } - opts := &CreateCommentOptions{ - Type: CommentTypeChangeTitle, - Doer: doer, - Repo: issue.Repo, - Issue: issue, - OldTitle: oldTitle, - NewTitle: issue.Title, - } - if _, err = CreateComment(ctx, opts); err != nil { - return fmt.Errorf("createComment: %w", err) - } - if err = issue.AddCrossReferences(ctx, doer, true); err != nil { - return err - } + if err = issue.LoadRepo(ctx); err != nil { + return fmt.Errorf("loadRepo: %w", err) + } - return committer.Commit() + opts := &CreateCommentOptions{ + Type: CommentTypeChangeTitle, + Doer: doer, + Repo: issue.Repo, + Issue: issue, + OldTitle: oldTitle, + NewTitle: issue.Title, + } + if _, err = CreateComment(ctx, opts); err != nil { + return fmt.Errorf("createComment: %w", err) + } + return issue.AddCrossReferences(ctx, doer, true) + }) } // ChangeIssueRef changes the branch of this issue, as the given user. func ChangeIssueRef(ctx context.Context, issue *Issue, doer *user_model.User, oldRef string) (err error) { - ctx, committer, err := db.TxContext(ctx) - if err != nil { - return err - } - defer committer.Close() - - if err = UpdateIssueCols(ctx, issue, "ref"); err != nil { - return fmt.Errorf("updateIssueCols: %w", err) - } - - if err = issue.LoadRepo(ctx); err != nil { - return fmt.Errorf("loadRepo: %w", err) - } - oldRefFriendly := strings.TrimPrefix(oldRef, git.BranchPrefix) - newRefFriendly := strings.TrimPrefix(issue.Ref, git.BranchPrefix) + return db.WithTx(ctx, func(ctx context.Context) error { + if err = UpdateIssueCols(ctx, issue, "ref"); err != nil { + return fmt.Errorf("updateIssueCols: %w", err) + } - opts := &CreateCommentOptions{ - Type: CommentTypeChangeIssueRef, - Doer: doer, - Repo: issue.Repo, - Issue: issue, - OldRef: oldRefFriendly, - NewRef: newRefFriendly, - } - if _, err = CreateComment(ctx, opts); err != nil { - return fmt.Errorf("createComment: %w", err) - } + if err = issue.LoadRepo(ctx); err != nil { + return fmt.Errorf("loadRepo: %w", err) + } + oldRefFriendly := strings.TrimPrefix(oldRef, git.BranchPrefix) + newRefFriendly := strings.TrimPrefix(issue.Ref, git.BranchPrefix) - return committer.Commit() + opts := &CreateCommentOptions{ + Type: CommentTypeChangeIssueRef, + Doer: doer, + Repo: issue.Repo, + Issue: issue, + OldRef: oldRefFriendly, + NewRef: newRefFriendly, + } + if _, err = CreateComment(ctx, opts); err != nil { + return fmt.Errorf("createComment: %w", err) + } + return nil + }) } // AddDeletePRBranchComment adds delete branch comment for pull request issue @@ -295,64 +260,56 @@ func AddDeletePRBranchComment(ctx context.Context, doer *user_model.User, repo * // UpdateIssueAttachments update attachments by UUIDs for the issue func UpdateIssueAttachments(ctx context.Context, issueID int64, uuids []string) (err error) { - ctx, committer, err := db.TxContext(ctx) - if err != nil { - return err - } - defer committer.Close() - attachments, err := repo_model.GetAttachmentsByUUIDs(ctx, uuids) - if err != nil { - return fmt.Errorf("getAttachmentsByUUIDs [uuids: %v]: %w", uuids, err) - } - for i := range attachments { - attachments[i].IssueID = issueID - if err := repo_model.UpdateAttachment(ctx, attachments[i]); err != nil { - return fmt.Errorf("update attachment [id: %d]: %w", attachments[i].ID, err) + return db.WithTx(ctx, func(ctx context.Context) error { + attachments, err := repo_model.GetAttachmentsByUUIDs(ctx, uuids) + if err != nil { + return fmt.Errorf("getAttachmentsByUUIDs [uuids: %v]: %w", uuids, err) } - } - return committer.Commit() + for i := range attachments { + attachments[i].IssueID = issueID + if err := repo_model.UpdateAttachment(ctx, attachments[i]); err != nil { + return fmt.Errorf("update attachment [id: %d]: %w", attachments[i].ID, err) + } + } + return nil + }) } // ChangeIssueContent changes issue content, as the given user. func ChangeIssueContent(ctx context.Context, issue *Issue, doer *user_model.User, content string, contentVersion int) (err error) { - ctx, committer, err := db.TxContext(ctx) - if err != nil { - return err - } - defer committer.Close() - - hasContentHistory, err := HasIssueContentHistory(ctx, issue.ID, 0) - if err != nil { - return fmt.Errorf("HasIssueContentHistory: %w", err) - } - if !hasContentHistory { - if err = SaveIssueContentHistory(ctx, issue.PosterID, issue.ID, 0, - issue.CreatedUnix, issue.Content, true); err != nil { - return fmt.Errorf("SaveIssueContentHistory: %w", err) + return db.WithTx(ctx, func(ctx context.Context) error { + hasContentHistory, err := HasIssueContentHistory(ctx, issue.ID, 0) + if err != nil { + return fmt.Errorf("HasIssueContentHistory: %w", err) + } + if !hasContentHistory { + if err = SaveIssueContentHistory(ctx, issue.PosterID, issue.ID, 0, + issue.CreatedUnix, issue.Content, true); err != nil { + return fmt.Errorf("SaveIssueContentHistory: %w", err) + } } - } - - issue.Content = content - issue.ContentVersion = contentVersion + 1 - affected, err := db.GetEngine(ctx).ID(issue.ID).Cols("content", "content_version").Where("content_version = ?", contentVersion).Update(issue) - if err != nil { - return err - } - if affected == 0 { - return ErrIssueAlreadyChanged - } + issue.Content = content + issue.ContentVersion = contentVersion + 1 - if err = SaveIssueContentHistory(ctx, doer.ID, issue.ID, 0, - timeutil.TimeStampNow(), issue.Content, false); err != nil { - return fmt.Errorf("SaveIssueContentHistory: %w", err) - } + affected, err := db.GetEngine(ctx).ID(issue.ID).Cols("content", "content_version").Where("content_version = ?", contentVersion).Update(issue) + if err != nil { + return err + } + if affected == 0 { + return ErrIssueAlreadyChanged + } - if err = issue.AddCrossReferences(ctx, doer, true); err != nil { - return fmt.Errorf("addCrossReferences: %w", err) - } + if err = SaveIssueContentHistory(ctx, doer.ID, issue.ID, 0, + timeutil.TimeStampNow(), issue.Content, false); err != nil { + return fmt.Errorf("SaveIssueContentHistory: %w", err) + } - return committer.Commit() + if err = issue.AddCrossReferences(ctx, doer, true); err != nil { + return fmt.Errorf("addCrossReferences: %w", err) + } + return nil + }) } // NewIssueOptions represents the options of a new issue. @@ -512,23 +469,19 @@ func UpdateIssueDeadline(ctx context.Context, issue *Issue, deadlineUnix timeuti if issue.DeadlineUnix == deadlineUnix { return nil } - ctx, committer, err := db.TxContext(ctx) - if err != nil { - return err - } - defer committer.Close() - // Update the deadline - if err = UpdateIssueCols(ctx, &Issue{ID: issue.ID, DeadlineUnix: deadlineUnix}, "deadline_unix"); err != nil { - return err - } - - // Make the comment - if _, err = createDeadlineComment(ctx, doer, issue, deadlineUnix); err != nil { - return fmt.Errorf("createRemovedDueDateComment: %w", err) - } + return db.WithTx(ctx, func(ctx context.Context) error { + // Update the deadline + if err = UpdateIssueCols(ctx, &Issue{ID: issue.ID, DeadlineUnix: deadlineUnix}, "deadline_unix"); err != nil { + return err + } - return committer.Commit() + // Make the comment + if _, err = createDeadlineComment(ctx, doer, issue, deadlineUnix); err != nil { + return fmt.Errorf("createRemovedDueDateComment: %w", err) + } + return nil + }) } // FindAndUpdateIssueMentions finds users mentioned in the given content string, and saves them in the database. diff --git a/models/issues/label.go b/models/issues/label.go index cfbe100926..25d6f1303e 100644 --- a/models/issues/label.go +++ b/models/issues/label.go @@ -209,24 +209,20 @@ func NewLabel(ctx context.Context, l *Label) error { // NewLabels creates new labels func NewLabels(ctx context.Context, labels ...*Label) error { - ctx, committer, err := db.TxContext(ctx) - if err != nil { - return err - } - defer committer.Close() - - for _, l := range labels { - color, err := label.NormalizeColor(l.Color) - if err != nil { - return err - } - l.Color = color + return db.WithTx(ctx, func(ctx context.Context) error { + for _, l := range labels { + color, err := label.NormalizeColor(l.Color) + if err != nil { + return err + } + l.Color = color - if err := db.Insert(ctx, l); err != nil { - return err + if err := db.Insert(ctx, l); err != nil { + return err + } } - } - return committer.Commit() + return nil + }) } // UpdateLabel updates label information. @@ -250,35 +246,26 @@ func DeleteLabel(ctx context.Context, id, labelID int64) error { return err } - ctx, committer, err := db.TxContext(ctx) - if err != nil { - return err - } - defer committer.Close() - - sess := db.GetEngine(ctx) - - if l.BelongsToOrg() && l.OrgID != id { - return nil - } - if l.BelongsToRepo() && l.RepoID != id { - return nil - } + return db.WithTx(ctx, func(ctx context.Context) error { + if l.BelongsToOrg() && l.OrgID != id { + return nil + } + if l.BelongsToRepo() && l.RepoID != id { + return nil + } - if _, err = db.DeleteByID[Label](ctx, labelID); err != nil { - return err - } else if _, err = sess. - Where("label_id = ?", labelID). - Delete(new(IssueLabel)); err != nil { - return err - } + if _, err = db.DeleteByID[Label](ctx, labelID); err != nil { + return err + } else if _, err = db.GetEngine(ctx). + Where("label_id = ?", labelID). + Delete(new(IssueLabel)); err != nil { + return err + } - // delete comments about now deleted label_id - if _, err = sess.Where("label_id = ?", labelID).Cols("label_id").Delete(&Comment{}); err != nil { + // delete comments about now deleted label_id + _, err = db.GetEngine(ctx).Where("label_id = ?", labelID).Cols("label_id").Delete(&Comment{}) return err - } - - return committer.Commit() + }) } // GetLabelByID returns a label by given ID. diff --git a/models/issues/milestone.go b/models/issues/milestone.go index 4c9bae58f7..373f39f4ff 100644 --- a/models/issues/milestone.go +++ b/models/issues/milestone.go @@ -105,22 +105,16 @@ func (m *Milestone) State() api.StateType { // NewMilestone creates new milestone of repository. func NewMilestone(ctx context.Context, m *Milestone) (err error) { - ctx, committer, err := db.TxContext(ctx) - if err != nil { - return err - } - defer committer.Close() + return db.WithTx(ctx, func(ctx context.Context) error { + m.Name = strings.TrimSpace(m.Name) - m.Name = strings.TrimSpace(m.Name) - - if err = db.Insert(ctx, m); err != nil { - return err - } + if err = db.Insert(ctx, m); err != nil { + return err + } - if _, err = db.Exec(ctx, "UPDATE `repository` SET num_milestones = num_milestones + 1 WHERE id = ?", m.RepoID); err != nil { + _, err = db.Exec(ctx, "UPDATE `repository` SET num_milestones = num_milestones + 1 WHERE id = ?", m.RepoID) return err - } - return committer.Commit() + }) } // HasMilestoneByRepoID returns if the milestone exists in the repository. @@ -155,28 +149,23 @@ func GetMilestoneByRepoIDANDName(ctx context.Context, repoID int64, name string) // UpdateMilestone updates information of given milestone. func UpdateMilestone(ctx context.Context, m *Milestone, oldIsClosed bool) error { - ctx, committer, err := db.TxContext(ctx) - if err != nil { - return err - } - defer committer.Close() - - if m.IsClosed && !oldIsClosed { - m.ClosedDateUnix = timeutil.TimeStampNow() - } - - if err := updateMilestone(ctx, m); err != nil { - return err - } + return db.WithTx(ctx, func(ctx context.Context) error { + if m.IsClosed && !oldIsClosed { + m.ClosedDateUnix = timeutil.TimeStampNow() + } - // if IsClosed changed, update milestone numbers of repository - if oldIsClosed != m.IsClosed { - if err := updateRepoMilestoneNum(ctx, m.RepoID); err != nil { + if err := updateMilestone(ctx, m); err != nil { return err } - } - return committer.Commit() + // if IsClosed changed, update milestone numbers of repository + if oldIsClosed != m.IsClosed { + if err := updateRepoMilestoneNum(ctx, m.RepoID); err != nil { + return err + } + } + return nil + }) } func updateMilestone(ctx context.Context, m *Milestone) error { @@ -213,44 +202,28 @@ func UpdateMilestoneCounters(ctx context.Context, id int64) error { // ChangeMilestoneStatusByRepoIDAndID changes a milestone open/closed status if the milestone ID is in the repo. func ChangeMilestoneStatusByRepoIDAndID(ctx context.Context, repoID, milestoneID int64, isClosed bool) error { - ctx, committer, err := db.TxContext(ctx) - if err != nil { - return err - } - defer committer.Close() - - m := &Milestone{ - ID: milestoneID, - RepoID: repoID, - } - - has, err := db.GetEngine(ctx).ID(milestoneID).Where("repo_id = ?", repoID).Get(m) - if err != nil { - return err - } else if !has { - return ErrMilestoneNotExist{ID: milestoneID, RepoID: repoID} - } + return db.WithTx(ctx, func(ctx context.Context) error { + m := &Milestone{ + ID: milestoneID, + RepoID: repoID, + } - if err := changeMilestoneStatus(ctx, m, isClosed); err != nil { - return err - } + has, err := db.GetEngine(ctx).ID(milestoneID).Where("repo_id = ?", repoID).Get(m) + if err != nil { + return err + } else if !has { + return ErrMilestoneNotExist{ID: milestoneID, RepoID: repoID} + } - return committer.Commit() + return changeMilestoneStatus(ctx, m, isClosed) + }) } // ChangeMilestoneStatus changes the milestone open/closed status. func ChangeMilestoneStatus(ctx context.Context, m *Milestone, isClosed bool) (err error) { - ctx, committer, err := db.TxContext(ctx) - if err != nil { - return err - } - defer committer.Close() - - if err := changeMilestoneStatus(ctx, m, isClosed); err != nil { - return err - } - - return committer.Commit() + return db.WithTx(ctx, func(ctx context.Context) error { + return changeMilestoneStatus(ctx, m, isClosed) + }) } func changeMilestoneStatus(ctx context.Context, m *Milestone, isClosed bool) error { @@ -284,40 +257,34 @@ func DeleteMilestoneByRepoID(ctx context.Context, repoID, id int64) error { return err } - ctx, committer, err := db.TxContext(ctx) - if err != nil { - return err - } - defer committer.Close() - - if _, err = db.DeleteByID[Milestone](ctx, m.ID); err != nil { - return err - } + return db.WithTx(ctx, func(ctx context.Context) error { + if _, err = db.DeleteByID[Milestone](ctx, m.ID); err != nil { + return err + } - numMilestones, err := db.Count[Milestone](ctx, FindMilestoneOptions{ - RepoID: repo.ID, - }) - if err != nil { - return err - } - numClosedMilestones, err := db.Count[Milestone](ctx, FindMilestoneOptions{ - RepoID: repo.ID, - IsClosed: optional.Some(true), - }) - if err != nil { - return err - } - repo.NumMilestones = int(numMilestones) - repo.NumClosedMilestones = int(numClosedMilestones) + numMilestones, err := db.Count[Milestone](ctx, FindMilestoneOptions{ + RepoID: repo.ID, + }) + if err != nil { + return err + } + numClosedMilestones, err := db.Count[Milestone](ctx, FindMilestoneOptions{ + RepoID: repo.ID, + IsClosed: optional.Some(true), + }) + if err != nil { + return err + } + repo.NumMilestones = int(numMilestones) + repo.NumClosedMilestones = int(numClosedMilestones) - if _, err = db.GetEngine(ctx).ID(repo.ID).Cols("num_milestones, num_closed_milestones").Update(repo); err != nil { - return err - } + if _, err = db.GetEngine(ctx).ID(repo.ID).Cols("num_milestones, num_closed_milestones").Update(repo); err != nil { + return err + } - if _, err = db.Exec(ctx, "UPDATE `issue` SET milestone_id = 0 WHERE milestone_id = ?", m.ID); err != nil { + _, err = db.Exec(ctx, "UPDATE `issue` SET milestone_id = 0 WHERE milestone_id = ?", m.ID) return err - } - return committer.Commit() + }) } func updateRepoMilestoneNum(ctx context.Context, repoID int64) error { @@ -360,22 +327,15 @@ func InsertMilestones(ctx context.Context, ms ...*Milestone) (err error) { return nil } - ctx, committer, err := db.TxContext(ctx) - if err != nil { - return err - } - defer committer.Close() - sess := db.GetEngine(ctx) - - // to return the id, so we should not use batch insert - for _, m := range ms { - if _, err = sess.NoAutoTime().Insert(m); err != nil { - return err + return db.WithTx(ctx, func(ctx context.Context) error { + // to return the id, so we should not use batch insert + for _, m := range ms { + if _, err = db.GetEngine(ctx).NoAutoTime().Insert(m); err != nil { + return err + } } - } - if _, err = db.Exec(ctx, "UPDATE `repository` SET num_milestones = num_milestones + ? WHERE id = ?", len(ms), ms[0].RepoID); err != nil { + _, err = db.Exec(ctx, "UPDATE `repository` SET num_milestones = num_milestones + ? WHERE id = ?", len(ms), ms[0].RepoID) return err - } - return committer.Commit() + }) } diff --git a/models/issues/pull.go b/models/issues/pull.go index 4c25b6f0c8..00d7bfe1ca 100644 --- a/models/issues/pull.go +++ b/models/issues/pull.go @@ -364,17 +364,10 @@ func (pr *PullRequest) GetApprovers(ctx context.Context) string { func (pr *PullRequest) getReviewedByLines(ctx context.Context, writer io.Writer) error { maxReviewers := setting.Repository.PullRequest.DefaultMergeMessageMaxApprovers - if maxReviewers == 0 { return nil } - ctx, committer, err := db.TxContext(ctx) - if err != nil { - return err - } - defer committer.Close() - // Note: This doesn't page as we only expect a very limited number of reviews reviews, err := FindLatestReviews(ctx, FindReviewOptions{ Types: []ReviewType{ReviewTypeApprove}, @@ -410,7 +403,7 @@ func (pr *PullRequest) getReviewedByLines(ctx context.Context, writer io.Writer) } reviewersWritten++ } - return committer.Commit() + return nil } // GetGitHeadRefName returns git ref for hidden pull request branch @@ -464,45 +457,36 @@ func (pr *PullRequest) IsFromFork() bool { // NewPullRequest creates new pull request with labels for repository. func NewPullRequest(ctx context.Context, repo *repo_model.Repository, issue *Issue, labelIDs []int64, uuids []string, pr *PullRequest) (err error) { - ctx, committer, err := db.TxContext(ctx) - if err != nil { - return err - } - defer committer.Close() - - idx, err := db.GetNextResourceIndex(ctx, "issue_index", repo.ID) - if err != nil { - return fmt.Errorf("generate pull request index failed: %w", err) - } - - issue.Index = idx - issue.Title = util.EllipsisDisplayString(issue.Title, 255) - - if err = NewIssueWithIndex(ctx, issue.Poster, NewIssueOptions{ - Repo: repo, - Issue: issue, - LabelIDs: labelIDs, - Attachments: uuids, - IsPull: true, - }); err != nil { - if repo_model.IsErrUserDoesNotHaveAccessToRepo(err) || IsErrNewIssueInsert(err) { - return err + return db.WithTx(ctx, func(ctx context.Context) error { + idx, err := db.GetNextResourceIndex(ctx, "issue_index", repo.ID) + if err != nil { + return fmt.Errorf("generate pull request index failed: %w", err) } - return fmt.Errorf("newIssue: %w", err) - } - - pr.Index = issue.Index - pr.BaseRepo = repo - pr.IssueID = issue.ID - if err = db.Insert(ctx, pr); err != nil { - return fmt.Errorf("insert pull repo: %w", err) - } - if err = committer.Commit(); err != nil { - return fmt.Errorf("Commit: %w", err) - } + issue.Index = idx + issue.Title = util.EllipsisDisplayString(issue.Title, 255) + + if err = NewIssueWithIndex(ctx, issue.Poster, NewIssueOptions{ + Repo: repo, + Issue: issue, + LabelIDs: labelIDs, + Attachments: uuids, + IsPull: true, + }); err != nil { + if repo_model.IsErrUserDoesNotHaveAccessToRepo(err) || IsErrNewIssueInsert(err) { + return err + } + return fmt.Errorf("newIssue: %w", err) + } - return nil + pr.Index = issue.Index + pr.BaseRepo = repo + pr.IssueID = issue.ID + if err = db.Insert(ctx, pr); err != nil { + return fmt.Errorf("insert pull repo: %w", err) + } + return nil + }) } // ErrUserMustCollaborator represents an error that the user must be a collaborator to a given repo. @@ -977,22 +961,18 @@ func TokenizeCodeOwnersLine(line string) []string { // InsertPullRequests inserted pull requests func InsertPullRequests(ctx context.Context, prs ...*PullRequest) error { - ctx, committer, err := db.TxContext(ctx) - if err != nil { - return err - } - defer committer.Close() - sess := db.GetEngine(ctx) - for _, pr := range prs { - if err := insertIssue(ctx, pr.Issue); err != nil { - return err - } - pr.IssueID = pr.Issue.ID - if _, err := sess.NoAutoTime().Insert(pr); err != nil { - return err + return db.WithTx(ctx, func(ctx context.Context) error { + for _, pr := range prs { + if err := insertIssue(ctx, pr.Issue); err != nil { + return err + } + pr.IssueID = pr.Issue.ID + if _, err := db.GetEngine(ctx).NoAutoTime().Insert(pr); err != nil { + return err + } } - } - return committer.Commit() + return nil + }) } // GetPullRequestByMergedCommit returns a merged pull request by the given commit diff --git a/models/issues/reaction.go b/models/issues/reaction.go index f24001fd23..3b5ad6d7ab 100644 --- a/models/issues/reaction.go +++ b/models/issues/reaction.go @@ -224,21 +224,9 @@ func CreateReaction(ctx context.Context, opts *ReactionOptions) (*Reaction, erro return nil, ErrForbiddenIssueReaction{opts.Type} } - ctx, committer, err := db.TxContext(ctx) - if err != nil { - return nil, err - } - defer committer.Close() - - reaction, err := createReaction(ctx, opts) - if err != nil { - return reaction, err - } - - if err := committer.Commit(); err != nil { - return nil, err - } - return reaction, nil + return db.WithTx2(ctx, func(ctx context.Context) (*Reaction, error) { + return createReaction(ctx, opts) + }) } // DeleteReaction deletes reaction for issue or comment. diff --git a/models/issues/review.go b/models/issues/review.go index 71fdb7456f..b758fa5ffa 100644 --- a/models/issues/review.go +++ b/models/issues/review.go @@ -334,54 +334,51 @@ func IsOfficialReviewerTeam(ctx context.Context, issue *Issue, team *organizatio // CreateReview creates a new review based on opts func CreateReview(ctx context.Context, opts CreateReviewOptions) (*Review, error) { - ctx, committer, err := db.TxContext(ctx) - if err != nil { - return nil, err - } - defer committer.Close() - sess := db.GetEngine(ctx) - - review := &Review{ - Issue: opts.Issue, - IssueID: opts.Issue.ID, - Reviewer: opts.Reviewer, - ReviewerTeam: opts.ReviewerTeam, - Content: opts.Content, - Official: opts.Official, - CommitID: opts.CommitID, - Stale: opts.Stale, - } + return db.WithTx2(ctx, func(ctx context.Context) (*Review, error) { + sess := db.GetEngine(ctx) + + review := &Review{ + Issue: opts.Issue, + IssueID: opts.Issue.ID, + Reviewer: opts.Reviewer, + ReviewerTeam: opts.ReviewerTeam, + Content: opts.Content, + Official: opts.Official, + CommitID: opts.CommitID, + Stale: opts.Stale, + } - if opts.Reviewer != nil { - review.Type = opts.Type - review.ReviewerID = opts.Reviewer.ID + if opts.Reviewer != nil { + review.Type = opts.Type + review.ReviewerID = opts.Reviewer.ID - reviewCond := builder.Eq{"reviewer_id": opts.Reviewer.ID, "issue_id": opts.Issue.ID} - // make sure user review requests are cleared - if opts.Type != ReviewTypePending { - if _, err := sess.Where(reviewCond.And(builder.Eq{"type": ReviewTypeRequest})).Delete(new(Review)); err != nil { - return nil, err + reviewCond := builder.Eq{"reviewer_id": opts.Reviewer.ID, "issue_id": opts.Issue.ID} + // make sure user review requests are cleared + if opts.Type != ReviewTypePending { + if _, err := sess.Where(reviewCond.And(builder.Eq{"type": ReviewTypeRequest})).Delete(new(Review)); err != nil { + return nil, err + } } - } - // make sure if the created review gets dismissed no old review surface - // other types can be ignored, as they don't affect branch protection - if opts.Type == ReviewTypeApprove || opts.Type == ReviewTypeReject { - if _, err := sess.Where(reviewCond.And(builder.In("type", ReviewTypeApprove, ReviewTypeReject))). - Cols("dismissed").Update(&Review{Dismissed: true}); err != nil { - return nil, err + // make sure if the created review gets dismissed no old review surface + // other types can be ignored, as they don't affect branch protection + if opts.Type == ReviewTypeApprove || opts.Type == ReviewTypeReject { + if _, err := sess.Where(reviewCond.And(builder.In("type", ReviewTypeApprove, ReviewTypeReject))). + Cols("dismissed").Update(&Review{Dismissed: true}); err != nil { + return nil, err + } } + } else if opts.ReviewerTeam != nil { + review.Type = ReviewTypeRequest + review.ReviewerTeamID = opts.ReviewerTeam.ID + } else { + return nil, errors.New("provide either reviewer or reviewer team") } - } else if opts.ReviewerTeam != nil { - review.Type = ReviewTypeRequest - review.ReviewerTeamID = opts.ReviewerTeam.ID - } else { - return nil, errors.New("provide either reviewer or reviewer team") - } - if _, err := sess.Insert(review); err != nil { - return nil, err - } - return review, committer.Commit() + if _, err := sess.Insert(review); err != nil { + return nil, err + } + return review, nil + }) } // GetCurrentReview returns the current pending review of reviewer for given issue @@ -605,168 +602,152 @@ func DismissReview(ctx context.Context, review *Review, isDismiss bool) (err err // InsertReviews inserts review and review comments func InsertReviews(ctx context.Context, reviews []*Review) error { - ctx, committer, err := db.TxContext(ctx) - if err != nil { - return err - } - defer committer.Close() - sess := db.GetEngine(ctx) + return db.WithTx(ctx, func(ctx context.Context) error { + sess := db.GetEngine(ctx) - for _, review := range reviews { - if _, err := sess.NoAutoTime().Insert(review); err != nil { - return err - } + for _, review := range reviews { + if _, err := sess.NoAutoTime().Insert(review); err != nil { + return err + } - if _, err := sess.NoAutoTime().Insert(&Comment{ - Type: CommentTypeReview, - Content: review.Content, - PosterID: review.ReviewerID, - OriginalAuthor: review.OriginalAuthor, - OriginalAuthorID: review.OriginalAuthorID, - IssueID: review.IssueID, - ReviewID: review.ID, - CreatedUnix: review.CreatedUnix, - UpdatedUnix: review.UpdatedUnix, - }); err != nil { - return err - } + if _, err := sess.NoAutoTime().Insert(&Comment{ + Type: CommentTypeReview, + Content: review.Content, + PosterID: review.ReviewerID, + OriginalAuthor: review.OriginalAuthor, + OriginalAuthorID: review.OriginalAuthorID, + IssueID: review.IssueID, + ReviewID: review.ID, + CreatedUnix: review.CreatedUnix, + UpdatedUnix: review.UpdatedUnix, + }); err != nil { + return err + } - for _, c := range review.Comments { - c.ReviewID = review.ID - } + for _, c := range review.Comments { + c.ReviewID = review.ID + } - if len(review.Comments) > 0 { - if _, err := sess.NoAutoTime().Insert(review.Comments); err != nil { - return err + if len(review.Comments) > 0 { + if _, err := sess.NoAutoTime().Insert(review.Comments); err != nil { + return err + } } - } - if err := UpdateIssueNumComments(ctx, review.IssueID); err != nil { - return err + if err := UpdateIssueNumComments(ctx, review.IssueID); err != nil { + return err + } } - } - - return committer.Commit() + return nil + }) } // AddReviewRequest add a review request from one reviewer func AddReviewRequest(ctx context.Context, issue *Issue, reviewer, doer *user_model.User) (*Comment, error) { - ctx, committer, err := db.TxContext(ctx) - if err != nil { - return nil, err - } - defer committer.Close() - sess := db.GetEngine(ctx) + return db.WithTx2(ctx, func(ctx context.Context) (*Comment, error) { + sess := db.GetEngine(ctx) - review, err := GetReviewByIssueIDAndUserID(ctx, issue.ID, reviewer.ID) - if err != nil && !IsErrReviewNotExist(err) { - return nil, err - } - - if review != nil { - // skip it when reviewer has been request to review - if review.Type == ReviewTypeRequest { - return nil, committer.Commit() // still commit the transaction, or committer.Close() will rollback it, even if it's a reused transaction. - } - - if issue.IsClosed { - return nil, ErrReviewRequestOnClosedPR{} + review, err := GetReviewByIssueIDAndUserID(ctx, issue.ID, reviewer.ID) + if err != nil && !IsErrReviewNotExist(err) { + return nil, err } - if issue.IsPull { - if err := issue.LoadPullRequest(ctx); err != nil { - return nil, err + if review != nil { + // skip it when reviewer has been request to review + if review.Type == ReviewTypeRequest { + return nil, nil // still commit the transaction, or committer.Close() will rollback it, even if it's a reused transaction. } - if issue.PullRequest.HasMerged { + + if issue.IsClosed { return nil, ErrReviewRequestOnClosedPR{} } + + if issue.IsPull { + if err := issue.LoadPullRequest(ctx); err != nil { + return nil, err + } + if issue.PullRequest.HasMerged { + return nil, ErrReviewRequestOnClosedPR{} + } + } } - } - // if the reviewer is an official reviewer, - // remove the official flag in the all previous reviews - official, err := IsOfficialReviewer(ctx, issue, reviewer) - if err != nil { - return nil, err - } else if official { - if _, err := sess.Exec("UPDATE `review` SET official=? WHERE issue_id=? AND reviewer_id=?", false, issue.ID, reviewer.ID); err != nil { + // if the reviewer is an official reviewer, + // remove the official flag in the all previous reviews + official, err := IsOfficialReviewer(ctx, issue, reviewer) + if err != nil { return nil, err + } else if official { + if _, err := sess.Exec("UPDATE `review` SET official=? WHERE issue_id=? AND reviewer_id=?", false, issue.ID, reviewer.ID); err != nil { + return nil, err + } } - } - review, err = CreateReview(ctx, CreateReviewOptions{ - Type: ReviewTypeRequest, - Issue: issue, - Reviewer: reviewer, - Official: official, - Stale: false, - }) - if err != nil { - return nil, err - } + review, err = CreateReview(ctx, CreateReviewOptions{ + Type: ReviewTypeRequest, + Issue: issue, + Reviewer: reviewer, + Official: official, + Stale: false, + }) + if err != nil { + return nil, err + } - comment, err := CreateComment(ctx, &CreateCommentOptions{ - Type: CommentTypeReviewRequest, - Doer: doer, - Repo: issue.Repo, - Issue: issue, - RemovedAssignee: false, // Use RemovedAssignee as !isRequest - AssigneeID: reviewer.ID, // Use AssigneeID as reviewer ID - ReviewID: review.ID, - }) - if err != nil { - return nil, err - } + comment, err := CreateComment(ctx, &CreateCommentOptions{ + Type: CommentTypeReviewRequest, + Doer: doer, + Repo: issue.Repo, + Issue: issue, + RemovedAssignee: false, // Use RemovedAssignee as !isRequest + AssigneeID: reviewer.ID, // Use AssigneeID as reviewer ID + ReviewID: review.ID, + }) + if err != nil { + return nil, err + } - // func caller use the created comment to retrieve created review too. - comment.Review = review + // func caller use the created comment to retrieve created review too. + comment.Review = review - return comment, committer.Commit() + return comment, nil + }) } // RemoveReviewRequest remove a review request from one reviewer func RemoveReviewRequest(ctx context.Context, issue *Issue, reviewer, doer *user_model.User) (*Comment, error) { - ctx, committer, err := db.TxContext(ctx) - if err != nil { - return nil, err - } - defer committer.Close() - - review, err := GetReviewByIssueIDAndUserID(ctx, issue.ID, reviewer.ID) - if err != nil && !IsErrReviewNotExist(err) { - return nil, err - } + return db.WithTx2(ctx, func(ctx context.Context) (*Comment, error) { + review, err := GetReviewByIssueIDAndUserID(ctx, issue.ID, reviewer.ID) + if err != nil && !IsErrReviewNotExist(err) { + return nil, err + } - if review == nil || review.Type != ReviewTypeRequest { - return nil, nil - } + if review == nil || review.Type != ReviewTypeRequest { + return nil, nil + } - if _, err = db.DeleteByBean(ctx, review); err != nil { - return nil, err - } + if _, err = db.DeleteByBean(ctx, review); err != nil { + return nil, err + } - official, err := IsOfficialReviewer(ctx, issue, reviewer) - if err != nil { - return nil, err - } else if official { - if err := restoreLatestOfficialReview(ctx, issue.ID, reviewer.ID); err != nil { + official, err := IsOfficialReviewer(ctx, issue, reviewer) + if err != nil { return nil, err + } else if official { + if err := restoreLatestOfficialReview(ctx, issue.ID, reviewer.ID); err != nil { + return nil, err + } } - } - comment, err := CreateComment(ctx, &CreateCommentOptions{ - Type: CommentTypeReviewRequest, - Doer: doer, - Repo: issue.Repo, - Issue: issue, - RemovedAssignee: true, // Use RemovedAssignee as !isRequest - AssigneeID: reviewer.ID, // Use AssigneeID as reviewer ID + return CreateComment(ctx, &CreateCommentOptions{ + Type: CommentTypeReviewRequest, + Doer: doer, + Repo: issue.Repo, + Issue: issue, + RemovedAssignee: true, // Use RemovedAssignee as !isRequest + AssigneeID: reviewer.ID, // Use AssigneeID as reviewer ID + }) }) - if err != nil { - return nil, err - } - - return comment, committer.Commit() } // Recalculate the latest official review for reviewer @@ -787,120 +768,112 @@ func restoreLatestOfficialReview(ctx context.Context, issueID, reviewerID int64) // AddTeamReviewRequest add a review request from one team func AddTeamReviewRequest(ctx context.Context, issue *Issue, reviewer *organization.Team, doer *user_model.User) (*Comment, error) { - ctx, committer, err := db.TxContext(ctx) - if err != nil { - return nil, err - } - defer committer.Close() + return db.WithTx2(ctx, func(ctx context.Context) (*Comment, error) { + review, err := GetTeamReviewerByIssueIDAndTeamID(ctx, issue.ID, reviewer.ID) + if err != nil && !IsErrReviewNotExist(err) { + return nil, err + } - review, err := GetTeamReviewerByIssueIDAndTeamID(ctx, issue.ID, reviewer.ID) - if err != nil && !IsErrReviewNotExist(err) { - return nil, err - } + // This team already has been requested to review - therefore skip this. + if review != nil { + return nil, nil + } - // This team already has been requested to review - therefore skip this. - if review != nil { - return nil, nil - } + official, err := IsOfficialReviewerTeam(ctx, issue, reviewer) + if err != nil { + return nil, fmt.Errorf("isOfficialReviewerTeam(): %w", err) + } else if !official { + if official, err = IsOfficialReviewer(ctx, issue, doer); err != nil { + return nil, fmt.Errorf("isOfficialReviewer(): %w", err) + } + } - official, err := IsOfficialReviewerTeam(ctx, issue, reviewer) - if err != nil { - return nil, fmt.Errorf("isOfficialReviewerTeam(): %w", err) - } else if !official { - if official, err = IsOfficialReviewer(ctx, issue, doer); err != nil { - return nil, fmt.Errorf("isOfficialReviewer(): %w", err) + if review, err = CreateReview(ctx, CreateReviewOptions{ + Type: ReviewTypeRequest, + Issue: issue, + ReviewerTeam: reviewer, + Official: official, + Stale: false, + }); err != nil { + return nil, err } - } - if review, err = CreateReview(ctx, CreateReviewOptions{ - Type: ReviewTypeRequest, - Issue: issue, - ReviewerTeam: reviewer, - Official: official, - Stale: false, - }); err != nil { - return nil, err - } + if official { + if _, err := db.Exec(ctx, "UPDATE `review` SET official=? WHERE issue_id=? AND reviewer_team_id=?", false, issue.ID, reviewer.ID); err != nil { + return nil, err + } + } - if official { - if _, err := db.Exec(ctx, "UPDATE `review` SET official=? WHERE issue_id=? AND reviewer_team_id=?", false, issue.ID, reviewer.ID); err != nil { - return nil, err + comment, err := CreateComment(ctx, &CreateCommentOptions{ + Type: CommentTypeReviewRequest, + Doer: doer, + Repo: issue.Repo, + Issue: issue, + RemovedAssignee: false, // Use RemovedAssignee as !isRequest + AssigneeTeamID: reviewer.ID, // Use AssigneeTeamID as reviewer team ID + ReviewID: review.ID, + }) + if err != nil { + return nil, fmt.Errorf("CreateComment(): %w", err) } - } - comment, err := CreateComment(ctx, &CreateCommentOptions{ - Type: CommentTypeReviewRequest, - Doer: doer, - Repo: issue.Repo, - Issue: issue, - RemovedAssignee: false, // Use RemovedAssignee as !isRequest - AssigneeTeamID: reviewer.ID, // Use AssigneeTeamID as reviewer team ID - ReviewID: review.ID, + return comment, nil }) - if err != nil { - return nil, fmt.Errorf("CreateComment(): %w", err) - } - - return comment, committer.Commit() } // RemoveTeamReviewRequest remove a review request from one team func RemoveTeamReviewRequest(ctx context.Context, issue *Issue, reviewer *organization.Team, doer *user_model.User) (*Comment, error) { - ctx, committer, err := db.TxContext(ctx) - if err != nil { - return nil, err - } - defer committer.Close() - - review, err := GetTeamReviewerByIssueIDAndTeamID(ctx, issue.ID, reviewer.ID) - if err != nil && !IsErrReviewNotExist(err) { - return nil, err - } - - if review == nil { - return nil, nil - } - - if _, err = db.DeleteByBean(ctx, review); err != nil { - return nil, err - } + return db.WithTx2(ctx, func(ctx context.Context) (*Comment, error) { + review, err := GetTeamReviewerByIssueIDAndTeamID(ctx, issue.ID, reviewer.ID) + if err != nil && !IsErrReviewNotExist(err) { + return nil, err + } - official, err := IsOfficialReviewerTeam(ctx, issue, reviewer) - if err != nil { - return nil, fmt.Errorf("isOfficialReviewerTeam(): %w", err) - } + if review == nil { + return nil, nil + } - if official { - // recalculate which is the latest official review from that team - review, err := GetReviewByIssueIDAndUserID(ctx, issue.ID, -reviewer.ID) - if err != nil && !IsErrReviewNotExist(err) { + if _, err = db.DeleteByBean(ctx, review); err != nil { return nil, err } - if review != nil { - if _, err := db.Exec(ctx, "UPDATE `review` SET official=? WHERE id=?", true, review.ID); err != nil { + official, err := IsOfficialReviewerTeam(ctx, issue, reviewer) + if err != nil { + return nil, fmt.Errorf("isOfficialReviewerTeam(): %w", err) + } + + if official { + // recalculate which is the latest official review from that team + review, err := GetReviewByIssueIDAndUserID(ctx, issue.ID, -reviewer.ID) + if err != nil && !IsErrReviewNotExist(err) { return nil, err } + + if review != nil { + if _, err := db.Exec(ctx, "UPDATE `review` SET official=? WHERE id=?", true, review.ID); err != nil { + return nil, err + } + } } - } - if doer == nil { - return nil, committer.Commit() - } + if doer == nil { + return nil, nil + } - comment, err := CreateComment(ctx, &CreateCommentOptions{ - Type: CommentTypeReviewRequest, - Doer: doer, - Repo: issue.Repo, - Issue: issue, - RemovedAssignee: true, // Use RemovedAssignee as !isRequest - AssigneeTeamID: reviewer.ID, // Use AssigneeTeamID as reviewer team ID - }) - if err != nil { - return nil, fmt.Errorf("CreateComment(): %w", err) - } + comment, err := CreateComment(ctx, &CreateCommentOptions{ + Type: CommentTypeReviewRequest, + Doer: doer, + Repo: issue.Repo, + Issue: issue, + RemovedAssignee: true, // Use RemovedAssignee as !isRequest + AssigneeTeamID: reviewer.ID, // Use AssigneeTeamID as reviewer team ID + }) + if err != nil { + return nil, fmt.Errorf("CreateComment(): %w", err) + } - return comment, committer.Commit() + return comment, nil + }) } // MarkConversation Add or remove Conversation mark for a code comment @@ -966,61 +939,56 @@ func CanMarkConversation(ctx context.Context, issue *Issue, doer *user_model.Use // DeleteReview delete a review and it's code comments func DeleteReview(ctx context.Context, r *Review) error { - ctx, committer, err := db.TxContext(ctx) - if err != nil { - return err - } - defer committer.Close() - - if r.ID == 0 { - return errors.New("review is not allowed to be 0") - } - - if r.Type == ReviewTypeRequest { - return errors.New("review request can not be deleted using this method") - } + return db.WithTx(ctx, func(ctx context.Context) error { + if r.ID == 0 { + return errors.New("review is not allowed to be 0") + } - opts := FindCommentsOptions{ - Type: CommentTypeCode, - IssueID: r.IssueID, - ReviewID: r.ID, - } + if r.Type == ReviewTypeRequest { + return errors.New("review request can not be deleted using this method") + } - if _, err := db.Delete[Comment](ctx, opts); err != nil { - return err - } + opts := FindCommentsOptions{ + Type: CommentTypeCode, + IssueID: r.IssueID, + ReviewID: r.ID, + } - opts = FindCommentsOptions{ - Type: CommentTypeReview, - IssueID: r.IssueID, - ReviewID: r.ID, - } + if _, err := db.Delete[Comment](ctx, opts); err != nil { + return err + } - if _, err := db.Delete[Comment](ctx, opts); err != nil { - return err - } + opts = FindCommentsOptions{ + Type: CommentTypeReview, + IssueID: r.IssueID, + ReviewID: r.ID, + } - opts = FindCommentsOptions{ - Type: CommentTypeDismissReview, - IssueID: r.IssueID, - ReviewID: r.ID, - } + if _, err := db.Delete[Comment](ctx, opts); err != nil { + return err + } - if _, err := db.Delete[Comment](ctx, opts); err != nil { - return err - } + opts = FindCommentsOptions{ + Type: CommentTypeDismissReview, + IssueID: r.IssueID, + ReviewID: r.ID, + } - if _, err := db.DeleteByID[Review](ctx, r.ID); err != nil { - return err - } + if _, err := db.Delete[Comment](ctx, opts); err != nil { + return err + } - if r.Official { - if err := restoreLatestOfficialReview(ctx, r.IssueID, r.ReviewerID); err != nil { + if _, err := db.DeleteByID[Review](ctx, r.ID); err != nil { return err } - } - return committer.Commit() + if r.Official { + if err := restoreLatestOfficialReview(ctx, r.IssueID, r.ReviewerID); err != nil { + return err + } + } + return nil + }) } // GetCodeCommentsCount return count of CodeComments a Review has diff --git a/models/issues/tracked_time.go b/models/issues/tracked_time.go index 2afbe272ed..9c11881e44 100644 --- a/models/issues/tracked_time.go +++ b/models/issues/tracked_time.go @@ -168,35 +168,31 @@ func GetTrackedSeconds(ctx context.Context, opts FindTrackedTimesOptions) (track // AddTime will add the given time (in seconds) to the issue func AddTime(ctx context.Context, user *user_model.User, issue *Issue, amount int64, created time.Time) (*TrackedTime, error) { - ctx, committer, err := db.TxContext(ctx) - if err != nil { - return nil, err - } - defer committer.Close() - - t, err := addTime(ctx, user, issue, amount, created) - if err != nil { - return nil, err - } + return db.WithTx2(ctx, func(ctx context.Context) (*TrackedTime, error) { + t, err := addTime(ctx, user, issue, amount, created) + if err != nil { + return nil, err + } - if err := issue.LoadRepo(ctx); err != nil { - return nil, err - } + if err := issue.LoadRepo(ctx); err != nil { + return nil, err + } - if _, err := CreateComment(ctx, &CreateCommentOptions{ - Issue: issue, - Repo: issue.Repo, - Doer: user, - // Content before v1.21 did store the formatted string instead of seconds, - // so use "|" as delimiter to mark the new format - Content: fmt.Sprintf("|%d", amount), - Type: CommentTypeAddTimeManual, - TimeID: t.ID, - }); err != nil { - return nil, err - } + if _, err := CreateComment(ctx, &CreateCommentOptions{ + Issue: issue, + Repo: issue.Repo, + Doer: user, + // Content before v1.21 did store the formatted string instead of seconds, + // so use "|" as delimiter to mark the new format + Content: fmt.Sprintf("|%d", amount), + Type: CommentTypeAddTimeManual, + TimeID: t.ID, + }); err != nil { + return nil, err + } - return t, committer.Commit() + return t, nil + }) } func addTime(ctx context.Context, user *user_model.User, issue *Issue, amount int64, created time.Time) (*TrackedTime, error) { @@ -241,72 +237,58 @@ func TotalTimesForEachUser(ctx context.Context, options *FindTrackedTimesOptions // DeleteIssueUserTimes deletes times for issue func DeleteIssueUserTimes(ctx context.Context, issue *Issue, user *user_model.User) error { - ctx, committer, err := db.TxContext(ctx) - if err != nil { - return err - } - defer committer.Close() - - opts := FindTrackedTimesOptions{ - IssueID: issue.ID, - UserID: user.ID, - } + return db.WithTx(ctx, func(ctx context.Context) error { + opts := FindTrackedTimesOptions{ + IssueID: issue.ID, + UserID: user.ID, + } - removedTime, err := deleteTimes(ctx, opts) - if err != nil { - return err - } - if removedTime == 0 { - return db.ErrNotExist{Resource: "tracked_time"} - } + removedTime, err := deleteTimes(ctx, opts) + if err != nil { + return err + } + if removedTime == 0 { + return db.ErrNotExist{Resource: "tracked_time"} + } - if err := issue.LoadRepo(ctx); err != nil { - return err - } - if _, err := CreateComment(ctx, &CreateCommentOptions{ - Issue: issue, - Repo: issue.Repo, - Doer: user, - // Content before v1.21 did store the formatted string instead of seconds, - // so use "|" as delimiter to mark the new format - Content: fmt.Sprintf("|%d", removedTime), - Type: CommentTypeDeleteTimeManual, - }); err != nil { + if err := issue.LoadRepo(ctx); err != nil { + return err + } + _, err = CreateComment(ctx, &CreateCommentOptions{ + Issue: issue, + Repo: issue.Repo, + Doer: user, + // Content before v1.21 did store the formatted string instead of seconds, + // so use "|" as delimiter to mark the new format + Content: fmt.Sprintf("|%d", removedTime), + Type: CommentTypeDeleteTimeManual, + }) return err - } - - return committer.Commit() + }) } // DeleteTime delete a specific Time func DeleteTime(ctx context.Context, t *TrackedTime) error { - ctx, committer, err := db.TxContext(ctx) - if err != nil { - return err - } - defer committer.Close() - - if err := t.LoadAttributes(ctx); err != nil { - return err - } + return db.WithTx(ctx, func(ctx context.Context) error { + if err := t.LoadAttributes(ctx); err != nil { + return err + } - if err := deleteTime(ctx, t); err != nil { - return err - } + if err := deleteTime(ctx, t); err != nil { + return err + } - if _, err := CreateComment(ctx, &CreateCommentOptions{ - Issue: t.Issue, - Repo: t.Issue.Repo, - Doer: t.User, - // Content before v1.21 did store the formatted string instead of seconds, - // so use "|" as delimiter to mark the new format - Content: fmt.Sprintf("|%d", t.Time), - Type: CommentTypeDeleteTimeManual, - }); err != nil { + _, err := CreateComment(ctx, &CreateCommentOptions{ + Issue: t.Issue, + Repo: t.Issue.Repo, + Doer: t.User, + // Content before v1.21 did store the formatted string instead of seconds, + // so use "|" as delimiter to mark the new format + Content: fmt.Sprintf("|%d", t.Time), + Type: CommentTypeDeleteTimeManual, + }) return err - } - - return committer.Commit() + }) } func deleteTimes(ctx context.Context, opts FindTrackedTimesOptions) (removedTime int64, err error) { |