diff options
author | Anthony Wang | 2023-03-04 00:03:38 +0000 |
---|---|---|
committer | Anthony Wang | 2023-03-04 00:03:38 +0000 |
commit | b4640101f3c8938ed689743606a79601201141ca (patch) | |
tree | 8469fc5ef817560db646837b41417d50e2f8ebfc /models | |
parent | dc20c2832871f6462990751ea802e14b02bf41b0 (diff) | |
parent | 8540fc45b11eff9a73753ca139f8ea5c38509bf5 (diff) |
Merge remote-tracking branch 'origin/main' into forgejo-federation
Diffstat (limited to 'models')
28 files changed, 277 insertions, 91 deletions
diff --git a/models/actions/run.go b/models/actions/run.go index 14d191c81..a8d991471 100644 --- a/models/actions/run.go +++ b/models/actions/run.go @@ -32,11 +32,13 @@ type ActionRun struct { OwnerID int64 `xorm:"index"` WorkflowID string `xorm:"index"` // the name of workflow file Index int64 `xorm:"index unique(repo_index)"` // a unique number for each run of a repository - TriggerUserID int64 - TriggerUser *user_model.User `xorm:"-"` + TriggerUserID int64 `xorm:"index"` + TriggerUser *user_model.User `xorm:"-"` Ref string CommitSHA string IsForkPullRequest bool + NeedApproval bool // may need approval if it's a fork pull request + ApprovedBy int64 `xorm:"index"` // who approved Event webhook_module.HookEventType EventPayload string `xorm:"LONGTEXT"` Status Status `xorm:"index"` @@ -164,10 +166,6 @@ func InsertRun(ctx context.Context, run *ActionRun, jobs []*jobparser.SingleWork } run.Index = index - if run.Status.IsUnknown() { - run.Status = StatusWaiting - } - if err := db.Insert(ctx, run); err != nil { return err } @@ -191,7 +189,7 @@ func InsertRun(ctx context.Context, run *ActionRun, jobs []*jobparser.SingleWork job.EraseNeeds() payload, _ := v.Marshal() status := StatusWaiting - if len(needs) > 0 { + if len(needs) > 0 || run.NeedApproval { status = StatusBlocked } runJobs = append(runJobs, &ActionRunJob{ diff --git a/models/actions/run_list.go b/models/actions/run_list.go index f9d841722..bc69c6584 100644 --- a/models/actions/run_list.go +++ b/models/actions/run_list.go @@ -68,6 +68,8 @@ type FindRunOptions struct { OwnerID int64 IsClosed util.OptionalBool WorkflowFileName string + TriggerUserID int64 + Approved bool // not util.OptionalBool, it works only when it's true } func (opts FindRunOptions) toConds() builder.Cond { @@ -89,6 +91,12 @@ func (opts FindRunOptions) toConds() builder.Cond { if opts.WorkflowFileName != "" { cond = cond.And(builder.Eq{"workflow_id": opts.WorkflowFileName}) } + if opts.TriggerUserID > 0 { + cond = cond.And(builder.Eq{"trigger_user_id": opts.TriggerUserID}) + } + if opts.Approved { + cond = cond.And(builder.Gt{"approved_by": 0}) + } return cond } diff --git a/models/actions/status.go b/models/actions/status.go index 059cf9bc0..c97578f2a 100644 --- a/models/actions/status.go +++ b/models/actions/status.go @@ -82,6 +82,10 @@ func (s Status) IsRunning() bool { return s == StatusRunning } +func (s Status) IsBlocked() bool { + return s == StatusBlocked +} + // In returns whether s is one of the given statuses func (s Status) In(statuses ...Status) bool { for _, v := range statuses { diff --git a/models/activities/action.go b/models/activities/action.go index 2e845bf89..1412d2c05 100644 --- a/models/activities/action.go +++ b/models/activities/action.go @@ -380,14 +380,14 @@ type GetFeedsOptions struct { } // GetFeeds returns actions according to the provided options -func GetFeeds(ctx context.Context, opts GetFeedsOptions) (ActionList, error) { +func GetFeeds(ctx context.Context, opts GetFeedsOptions) (ActionList, int64, error) { if opts.RequestedUser == nil && opts.RequestedTeam == nil && opts.RequestedRepo == nil { - return nil, fmt.Errorf("need at least one of these filters: RequestedUser, RequestedTeam, RequestedRepo") + return nil, 0, fmt.Errorf("need at least one of these filters: RequestedUser, RequestedTeam, RequestedRepo") } cond, err := activityQueryCondition(opts) if err != nil { - return nil, err + return nil, 0, err } sess := db.GetEngine(ctx).Where(cond). @@ -398,16 +398,16 @@ func GetFeeds(ctx context.Context, opts GetFeedsOptions) (ActionList, error) { sess = db.SetSessionPagination(sess, &opts) actions := make([]*Action, 0, opts.PageSize) - - if err := sess.Desc("`action`.created_unix").Find(&actions); err != nil { - return nil, fmt.Errorf("Find: %w", err) + count, err := sess.Desc("`action`.created_unix").FindAndCount(&actions) + if err != nil { + return nil, 0, fmt.Errorf("FindAndCount: %w", err) } if err := ActionList(actions).loadAttributes(ctx); err != nil { - return nil, fmt.Errorf("LoadAttributes: %w", err) + return nil, 0, fmt.Errorf("LoadAttributes: %w", err) } - return actions, nil + return actions, count, nil } // ActivityReadable return whether doer can read activities of user diff --git a/models/activities/action_test.go b/models/activities/action_test.go index f37e58f68..2fd86bb8f 100644 --- a/models/activities/action_test.go +++ b/models/activities/action_test.go @@ -44,7 +44,7 @@ func TestGetFeeds(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) - actions, err := activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{ + actions, count, err := activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{ RequestedUser: user, Actor: user, IncludePrivate: true, @@ -56,8 +56,9 @@ func TestGetFeeds(t *testing.T) { assert.EqualValues(t, 1, actions[0].ID) assert.EqualValues(t, user.ID, actions[0].UserID) } + assert.Equal(t, int64(1), count) - actions, err = activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{ + actions, count, err = activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{ RequestedUser: user, Actor: user, IncludePrivate: false, @@ -65,6 +66,7 @@ func TestGetFeeds(t *testing.T) { }) assert.NoError(t, err) assert.Len(t, actions, 0) + assert.Equal(t, int64(0), count) } func TestGetFeedsForRepos(t *testing.T) { @@ -74,38 +76,42 @@ func TestGetFeedsForRepos(t *testing.T) { pubRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 8}) // private repo & no login - actions, err := activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{ + actions, count, err := activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{ RequestedRepo: privRepo, IncludePrivate: true, }) assert.NoError(t, err) assert.Len(t, actions, 0) + assert.Equal(t, int64(0), count) // public repo & no login - actions, err = activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{ + actions, count, err = activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{ RequestedRepo: pubRepo, IncludePrivate: true, }) assert.NoError(t, err) assert.Len(t, actions, 1) + assert.Equal(t, int64(1), count) // private repo and login - actions, err = activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{ + actions, count, err = activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{ RequestedRepo: privRepo, IncludePrivate: true, Actor: user, }) assert.NoError(t, err) assert.Len(t, actions, 1) + assert.Equal(t, int64(1), count) // public repo & login - actions, err = activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{ + actions, count, err = activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{ RequestedRepo: pubRepo, IncludePrivate: true, Actor: user, }) assert.NoError(t, err) assert.Len(t, actions, 1) + assert.Equal(t, int64(1), count) } func TestGetFeeds2(t *testing.T) { @@ -114,7 +120,7 @@ func TestGetFeeds2(t *testing.T) { org := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3}) user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) - actions, err := activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{ + actions, count, err := activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{ RequestedUser: org, Actor: user, IncludePrivate: true, @@ -127,8 +133,9 @@ func TestGetFeeds2(t *testing.T) { assert.EqualValues(t, 2, actions[0].ID) assert.EqualValues(t, org.ID, actions[0].UserID) } + assert.Equal(t, int64(1), count) - actions, err = activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{ + actions, count, err = activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{ RequestedUser: org, Actor: user, IncludePrivate: false, @@ -137,6 +144,7 @@ func TestGetFeeds2(t *testing.T) { }) assert.NoError(t, err) assert.Len(t, actions, 0) + assert.Equal(t, int64(0), count) } func TestActivityReadable(t *testing.T) { @@ -224,13 +232,14 @@ func TestGetFeedsCorrupted(t *testing.T) { RepoID: 1700, }) - actions, err := activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{ + actions, count, err := activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{ RequestedUser: user, Actor: user, IncludePrivate: true, }) assert.NoError(t, err) assert.Len(t, actions, 0) + assert.Equal(t, int64(0), count) } func TestConsistencyUpdateAction(t *testing.T) { diff --git a/models/activities/user_heatmap_test.go b/models/activities/user_heatmap_test.go index 34715ab6d..98df7b38a 100644 --- a/models/activities/user_heatmap_test.go +++ b/models/activities/user_heatmap_test.go @@ -73,7 +73,7 @@ func TestGetUserHeatmapDataByUser(t *testing.T) { } // get the action for comparison - actions, err := activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{ + actions, count, err := activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{ RequestedUser: user, Actor: doer, IncludePrivate: true, @@ -90,6 +90,7 @@ func TestGetUserHeatmapDataByUser(t *testing.T) { } assert.NoError(t, err) assert.Len(t, actions, contributions, "invalid action count: did the test data became too old?") + assert.Equal(t, count, int64(contributions)) assert.Equal(t, tc.CountResult, contributions, fmt.Sprintf("testcase '%s'", tc.desc)) // Test JSON rendering diff --git a/models/auth/oauth2.go b/models/auth/oauth2.go index 09d4bfc4e..bda0668c4 100644 --- a/models/auth/oauth2.go +++ b/models/auth/oauth2.go @@ -5,7 +5,6 @@ package auth import ( "context" - "crypto/sha256" "encoding/base32" "encoding/base64" "fmt" @@ -18,6 +17,7 @@ import ( "code.gitea.io/gitea/modules/util" uuid "github.com/google/uuid" + "github.com/minio/sha256-simd" "golang.org/x/crypto/bcrypt" "xorm.io/builder" "xorm.io/xorm" diff --git a/models/auth/token_scope.go b/models/auth/token_scope.go index c61c30649..38733a1c8 100644 --- a/models/auth/token_scope.go +++ b/models/auth/token_scope.go @@ -168,10 +168,23 @@ var allAccessTokenScopeBits = map[AccessTokenScope]AccessTokenScopeBitmap{ // Parse parses the scope string into a bitmap, thus removing possible duplicates. func (s AccessTokenScope) Parse() (AccessTokenScopeBitmap, error) { - list := strings.Split(string(s), ",") - var bitmap AccessTokenScopeBitmap - for _, v := range list { + + // The following is the more performant equivalent of 'for _, v := range strings.Split(remainingScope, ",")' as this is hot code + remainingScopes := string(s) + for len(remainingScopes) > 0 { + i := strings.IndexByte(remainingScopes, ',') + var v string + if i < 0 { + v = remainingScopes + remainingScopes = "" + } else if i+1 >= len(remainingScopes) { + v = remainingScopes[:i] + remainingScopes = "" + } else { + v = remainingScopes[:i] + remainingScopes = remainingScopes[i+1:] + } singleScope := AccessTokenScope(v) if singleScope == "" { continue @@ -187,9 +200,15 @@ func (s AccessTokenScope) Parse() (AccessTokenScopeBitmap, error) { } bitmap |= bits } + return bitmap, nil } +// StringSlice returns the AccessTokenScope as a []string +func (s AccessTokenScope) StringSlice() []string { + return strings.Split(string(s), ",") +} + // Normalize returns a normalized scope string without any duplicates. func (s AccessTokenScope) Normalize() (AccessTokenScope, error) { bitmap, err := s.Parse() diff --git a/models/auth/twofactor.go b/models/auth/twofactor.go index 5b3a9d011..751a281f7 100644 --- a/models/auth/twofactor.go +++ b/models/auth/twofactor.go @@ -5,7 +5,6 @@ package auth import ( "crypto/md5" - "crypto/sha256" "crypto/subtle" "encoding/base32" "encoding/base64" @@ -18,6 +17,7 @@ import ( "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/util" + "github.com/minio/sha256-simd" "github.com/pquerna/otp/totp" "golang.org/x/crypto/pbkdf2" ) diff --git a/models/avatars/avatar.go b/models/avatars/avatar.go index 6cf05dd28..265ee6428 100644 --- a/models/avatars/avatar.go +++ b/models/avatars/avatar.go @@ -153,7 +153,7 @@ func generateEmailAvatarLink(ctx context.Context, email string, size int, final return DefaultAvatarLink() } - enableFederatedAvatar := system_model.GetSettingBool(ctx, system_model.KeyPictureEnableFederatedAvatar) + enableFederatedAvatar := system_model.GetSettingWithCacheBool(ctx, system_model.KeyPictureEnableFederatedAvatar) var err error if enableFederatedAvatar && system_model.LibravatarService != nil { @@ -174,7 +174,7 @@ func generateEmailAvatarLink(ctx context.Context, email string, size int, final return urlStr } - disableGravatar := system_model.GetSettingBool(ctx, system_model.KeyPictureDisableGravatar) + disableGravatar := system_model.GetSettingWithCacheBool(ctx, system_model.KeyPictureDisableGravatar) if !disableGravatar { // copy GravatarSourceURL, because we will modify its Path. avatarURLCopy := *system_model.GravatarSourceURL diff --git a/models/avatars/avatar_test.go b/models/avatars/avatar_test.go index a3cb36d0e..59daaeb66 100644 --- a/models/avatars/avatar_test.go +++ b/models/avatars/avatar_test.go @@ -28,7 +28,7 @@ func enableGravatar(t *testing.T) { err := system_model.SetSettingNoVersion(db.DefaultContext, system_model.KeyPictureDisableGravatar, "false") assert.NoError(t, err) setting.GravatarSource = gravatarSource - err = system_model.Init() + err = system_model.Init(db.DefaultContext) assert.NoError(t, err) } diff --git a/models/db/context.go b/models/db/context.go index 4b3f7f0ee..670f6272a 100644 --- a/models/db/context.go +++ b/models/db/context.go @@ -209,7 +209,7 @@ func DecrByIDs(ctx context.Context, ids []int64, decrCol string, bean interface{ return err } -// DeleteBeans deletes all given beans, beans should contain delete conditions. +// DeleteBeans deletes all given beans, beans must contain delete conditions. func DeleteBeans(ctx context.Context, beans ...interface{}) (err error) { e := GetEngine(ctx) for i := range beans { @@ -220,6 +220,17 @@ func DeleteBeans(ctx context.Context, beans ...interface{}) (err error) { return nil } +// TruncateBeans deletes all given beans, beans may contain delete conditions. +func TruncateBeans(ctx context.Context, beans ...interface{}) (err error) { + e := GetEngine(ctx) + for i := range beans { + if _, err = e.Truncate(beans[i]); err != nil { + return err + } + } + return nil +} + // CountByBean counts the number of database records according non-empty fields of the bean as conditions. func CountByBean(ctx context.Context, bean interface{}) (int64, error) { return GetEngine(ctx).Count(bean) diff --git a/models/db/engine.go b/models/db/engine.go index 3d05fa8b6..5020101d4 100755 --- a/models/db/engine.go +++ b/models/db/engine.go @@ -38,6 +38,7 @@ type Engine interface { Count(...interface{}) (int64, error) Decr(column string, arg ...interface{}) *xorm.Session Delete(...interface{}) (int64, error) + Truncate(...interface{}) (int64, error) Exec(...interface{}) (sql.Result, error) Find(interface{}, ...interface{}) error Get(beans ...interface{}) (bool, error) diff --git a/models/db/list_options.go b/models/db/list.go index 2456f90f3..9fb4d0741 100644 --- a/models/db/list_options.go +++ b/models/db/list.go @@ -134,7 +134,7 @@ func Find[T any](ctx context.Context, opts FindOptions, objects *[]T) error { if !opts.IsListAll() { sess.Limit(opts.GetSkipTake()) } - return sess.Find(&objects) + return sess.Find(objects) } // Count represents a common count function which accept an options interface @@ -148,5 +148,5 @@ func FindAndCount[T any](ctx context.Context, opts FindOptions, objects *[]T) (i if !opts.IsListAll() { sess.Limit(opts.GetSkipTake()) } - return sess.FindAndCount(&objects) + return sess.FindAndCount(objects) } diff --git a/models/db/list_test.go b/models/db/list_test.go new file mode 100644 index 000000000..ffef1e494 --- /dev/null +++ b/models/db/list_test.go @@ -0,0 +1,48 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package db_test + +import ( + "testing" + + "code.gitea.io/gitea/models/db" + repo_model "code.gitea.io/gitea/models/repo" + "code.gitea.io/gitea/models/unittest" + + "github.com/stretchr/testify/assert" + "xorm.io/builder" +) + +type mockListOptions struct { + db.ListOptions +} + +func (opts *mockListOptions) IsListAll() bool { + return true +} + +func (opts *mockListOptions) ToConds() builder.Cond { + return builder.NewCond() +} + +func TestFind(t *testing.T) { + assert.NoError(t, unittest.PrepareTestDatabase()) + xe := unittest.GetXORMEngine() + assert.NoError(t, xe.Sync(&repo_model.RepoUnit{})) + + opts := mockListOptions{} + var repoUnits []repo_model.RepoUnit + err := db.Find(db.DefaultContext, &opts, &repoUnits) + assert.NoError(t, err) + assert.EqualValues(t, 83, len(repoUnits)) + + cnt, err := db.Count(db.DefaultContext, &opts, new(repo_model.RepoUnit)) + assert.NoError(t, err) + assert.EqualValues(t, 83, cnt) + + repoUnits = make([]repo_model.RepoUnit, 0, 10) + newCnt, err := db.FindAndCount(db.DefaultContext, &opts, &repoUnits) + assert.NoError(t, err) + assert.EqualValues(t, cnt, newCnt) +} diff --git a/models/issues/issue.go b/models/issues/issue.go index 99ec1437f..9fdb63cfa 100644 --- a/models/issues/issue.go +++ b/models/issues/issue.go @@ -253,13 +253,15 @@ 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 && issue.PullRequest == nil { - issue.PullRequest, err = GetPullRequestByIssueID(ctx, issue.ID) - if err != nil { - if IsErrPullRequestNotExist(err) { - return err + if issue.IsPull { + if issue.PullRequest == nil { + issue.PullRequest, err = GetPullRequestByIssueID(ctx, issue.ID) + if err != nil { + if IsErrPullRequestNotExist(err) { + return err + } + return fmt.Errorf("getPullRequestByIssueID [%d]: %w", issue.ID, err) } - return fmt.Errorf("getPullRequestByIssueID [%d]: %w", issue.ID, err) } issue.PullRequest.Issue = issue } @@ -349,7 +351,7 @@ func (issue *Issue) LoadAttributes(ctx context.Context) (err error) { return } - if err = issue.loadProject(ctx); err != nil { + if err = issue.LoadProject(ctx); err != nil { return } @@ -1148,6 +1150,7 @@ type IssuesOptions struct { //nolint PosterID int64 MentionedID int64 ReviewRequestedID int64 + ReviewedID int64 SubscriberID int64 MilestoneIDs []int64 ProjectID int64 @@ -1262,6 +1265,10 @@ func (opts *IssuesOptions) setupSessionNoLimit(sess *xorm.Session) { applyReviewRequestedCondition(sess, opts.ReviewRequestedID) } + if opts.ReviewedID > 0 { + applyReviewedCondition(sess, opts.ReviewedID) + } + if opts.SubscriberID > 0 { applySubscribedCondition(sess, opts.SubscriberID) } @@ -1432,6 +1439,36 @@ func applyReviewRequestedCondition(sess *xorm.Session, reviewRequestedID int64) reviewRequestedID, ReviewTypeApprove, ReviewTypeReject, ReviewTypeRequest, reviewRequestedID) } +func applyReviewedCondition(sess *xorm.Session, reviewedID int64) *xorm.Session { + // Query for pull requests where you are a reviewer or commenter, excluding + // any pull requests already returned by the the review requested filter. + notPoster := builder.Neq{"issue.poster_id": reviewedID} + reviewed := builder.In("issue.id", builder. + Select("issue_id"). + From("review"). + Where(builder.And( + builder.Neq{"type": ReviewTypeRequest}, + builder.Or( + builder.Eq{"reviewer_id": reviewedID}, + builder.In("reviewer_team_id", builder. + Select("team_id"). + From("team_user"). + Where(builder.Eq{"uid": reviewedID}), + ), + ), + )), + ) + commented := builder.In("issue.id", builder. + Select("issue_id"). + From("comment"). + Where(builder.And( + builder.Eq{"poster_id": reviewedID}, + builder.In("type", CommentTypeComment, CommentTypeCode, CommentTypeReview), + )), + ) + return sess.And(notPoster, builder.Or(reviewed, commented)) +} + func applySubscribedCondition(sess *xorm.Session, subscriberID int64) *xorm.Session { return sess.And( builder. @@ -1586,6 +1623,7 @@ type IssueStats struct { CreateCount int64 MentionCount int64 ReviewRequestedCount int64 + ReviewedCount int64 } // Filter modes. @@ -1595,6 +1633,7 @@ const ( FilterModeCreate FilterModeMention FilterModeReviewRequested + FilterModeReviewed FilterModeYourRepositories ) @@ -1608,6 +1647,7 @@ type IssueStatsOptions struct { MentionedID int64 PosterID int64 ReviewRequestedID int64 + ReviewedID int64 IsPull util.OptionalBool IssueIDs []int64 } @@ -1646,6 +1686,7 @@ func GetIssueStats(opts *IssueStatsOptions) (*IssueStats, error) { accum.CreateCount += stats.CreateCount accum.OpenCount += stats.MentionCount accum.ReviewRequestedCount += stats.ReviewRequestedCount + accum.ReviewedCount += stats.ReviewedCount i = chunk } return accum, nil @@ -1703,6 +1744,10 @@ func getIssueStatsChunk(opts *IssueStatsOptions, issueIDs []int64) (*IssueStats, applyReviewRequestedCondition(sess, opts.ReviewRequestedID) } + if opts.ReviewedID > 0 { + applyReviewedCondition(sess, opts.ReviewedID) + } + switch opts.IsPull { case util.OptionalBoolTrue: sess.And("issue.is_pull=?", true) @@ -1843,6 +1888,19 @@ func GetUserIssueStats(opts UserIssueStatsOptions) (*IssueStats, error) { if err != nil { return nil, err } + case FilterModeReviewed: + stats.OpenCount, err = applyReviewedCondition(sess(cond), opts.UserID). + And("issue.is_closed = ?", false). + Count(new(Issue)) + if err != nil { + return nil, err + } + stats.ClosedCount, err = applyReviewedCondition(sess(cond), opts.UserID). + And("issue.is_closed = ?", true). + Count(new(Issue)) + if err != nil { + return nil, err + } } cond = cond.And(builder.Eq{"issue.is_closed": opts.IsClosed}) @@ -1871,6 +1929,11 @@ func GetUserIssueStats(opts UserIssueStatsOptions) (*IssueStats, error) { return nil, err } + stats.ReviewedCount, err = applyReviewedCondition(sess(cond), opts.UserID).Count(new(Issue)) + if err != nil { + return nil, err + } + return stats, nil } diff --git a/models/issues/issue_project.go b/models/issues/issue_project.go index c9f4c9f53..04d12e055 100644 --- a/models/issues/issue_project.go +++ b/models/issues/issue_project.go @@ -13,11 +13,7 @@ import ( ) // LoadProject load the project the issue was assigned to -func (issue *Issue) LoadProject() (err error) { - return issue.loadProject(db.DefaultContext) -} - -func (issue *Issue) loadProject(ctx context.Context) (err error) { +func (issue *Issue) LoadProject(ctx context.Context) (err error) { if issue.Project == nil { var p project_model.Project if _, err = db.GetEngine(ctx).Table("project"). diff --git a/models/issues/label.go b/models/issues/label.go index 0dd12fb5c..90e4eb458 100644 --- a/models/issues/label.go +++ b/models/issues/label.go @@ -778,7 +778,7 @@ func CountOrphanedLabels(ctx context.Context) (int64, error) { norepo, err := db.GetEngine(ctx).Table("label"). Where(builder.And( builder.Gt{"repo_id": 0}, - builder.NotIn("repo_id", builder.Select("id").From("repository")), + builder.NotIn("repo_id", builder.Select("id").From("`repository`")), )). Count() if err != nil { @@ -788,7 +788,7 @@ func CountOrphanedLabels(ctx context.Context) (int64, error) { noorg, err := db.GetEngine(ctx).Table("label"). Where(builder.And( builder.Gt{"org_id": 0}, - builder.NotIn("org_id", builder.Select("id").From("user")), + builder.NotIn("org_id", builder.Select("id").From("`user`")), )). Count() if err != nil { @@ -809,7 +809,7 @@ func DeleteOrphanedLabels(ctx context.Context) error { if _, err := db.GetEngine(ctx). Where(builder.And( builder.Gt{"repo_id": 0}, - builder.NotIn("repo_id", builder.Select("id").From("repository")), + builder.NotIn("repo_id", builder.Select("id").From("`repository`")), )). Delete(Label{}); err != nil { return err @@ -819,7 +819,7 @@ func DeleteOrphanedLabels(ctx context.Context) error { if _, err := db.GetEngine(ctx). Where(builder.And( builder.Gt{"org_id": 0}, - builder.NotIn("org_id", builder.Select("id").From("user")), + builder.NotIn("org_id", builder.Select("id").From("`user`")), )). Delete(Label{}); err != nil { return err diff --git a/models/migrations/base/hash.go b/models/migrations/base/hash.go index 00fd1efd4..0debec272 100644 --- a/models/migrations/base/hash.go +++ b/models/migrations/base/hash.go @@ -4,9 +4,9 @@ package base import ( - "crypto/sha256" "encoding/hex" + "github.com/minio/sha256-simd" "golang.org/x/crypto/pbkdf2" ) diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index 989a1d6ae..585457e47 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -19,6 +19,7 @@ import ( "code.gitea.io/gitea/models/migrations/v1_17" "code.gitea.io/gitea/models/migrations/v1_18" "code.gitea.io/gitea/models/migrations/v1_19" + "code.gitea.io/gitea/models/migrations/v1_20" "code.gitea.io/gitea/models/migrations/v1_6" "code.gitea.io/gitea/models/migrations/v1_7" "code.gitea.io/gitea/models/migrations/v1_8" @@ -463,6 +464,9 @@ var migrations = []Migration{ NewMigration("Add exclusive label", v1_19.AddExclusiveLabel), // Gitea 1.19.0 ends at v244 + + // v244 -> v245 + NewMigration("Add NeedApproval to actions tables", v1_20.AddNeedApprovalToActionRun), } // GetCurrentDBVersion returns the current db version diff --git a/models/migrations/v1_14/v166.go b/models/migrations/v1_14/v166.go index f797930d6..de7626076 100644 --- a/models/migrations/v1_14/v166.go +++ b/models/migrations/v1_14/v166.go @@ -4,9 +4,9 @@ package v1_14 //nolint import ( - "crypto/sha256" "encoding/hex" + "github.com/minio/sha256-simd" "golang.org/x/crypto/argon2" "golang.org/x/crypto/bcrypt" "golang.org/x/crypto/pbkdf2" diff --git a/models/migrations/v1_20/v244.go b/models/migrations/v1_20/v244.go new file mode 100644 index 000000000..977566ad7 --- /dev/null +++ b/models/migrations/v1_20/v244.go @@ -0,0 +1,22 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_20 //nolint + +import ( + "xorm.io/xorm" +) + +func AddNeedApprovalToActionRun(x *xorm.Engine) error { + /* + New index: TriggerUserID + New fields: NeedApproval, ApprovedBy + */ + type ActionRun struct { + TriggerUserID int64 `xorm:"index"` + NeedApproval bool // may need approval if it's a fork pull request + ApprovedBy int64 `xorm:"index"` // who approved + } + + return x.Sync(new(ActionRun)) +} diff --git a/models/project/project.go b/models/project/project.go index 931ef4467..46b5c07c4 100644 --- a/models/project/project.go +++ b/models/project/project.go @@ -217,16 +217,8 @@ func CountProjects(ctx context.Context, opts SearchOptions) (int64, error) { // FindProjects returns a list of all projects that have been created in the repository func FindProjects(ctx context.Context, opts SearchOptions) ([]*Project, int64, error) { - e := db.GetEngine(ctx) + e := db.GetEngine(ctx).Where(opts.toConds()) projects := make([]*Project, 0, setting.UI.IssuePagingNum) - cond := opts.toConds() - - count, err := e.Where(cond).Count(new(Project)) - if err != nil { - return nil, 0, fmt.Errorf("Count: %w", err) - } - - e = e.Where(cond) if opts.Page > 0 { e = e.Limit(setting.UI.IssuePagingNum, (opts.Page-1)*setting.UI.IssuePagingNum) @@ -243,7 +235,8 @@ func FindProjects(ctx context.Context, opts SearchOptions) ([]*Project, int64, e e.Asc("created_unix") } - return projects, count, e.Find(&projects) + count, err := e.FindAndCount(&projects) + return projects, count, err } // NewProject creates a new Project diff --git a/models/repo.go b/models/repo.go index 38dc3f1ab..5a1e2e028 100644 --- a/models/repo.go +++ b/models/repo.go @@ -39,9 +39,9 @@ import ( var ItemsPerPage = 40 // Init initialize model -func Init() error { +func Init(ctx context.Context) error { unit.LoadUnitConfig() - return system_model.Init() + return system_model.Init(ctx) } // DeleteRepository deletes a repository for a user or organization. diff --git a/models/system/setting.go b/models/system/setting.go index 098d9a183..6af759dc8 100644 --- a/models/system/setting.go +++ b/models/system/setting.go @@ -79,8 +79,8 @@ func IsErrDataExpired(err error) bool { return ok } -// GetSettingNoCache returns specific setting without using the cache -func GetSettingNoCache(ctx context.Context, key string) (*Setting, error) { +// GetSetting returns specific setting without using the cache +func GetSetting(ctx context.Context, key string) (*Setting, error) { v, err := GetSettings(ctx, []string{key}) if err != nil { return nil, err @@ -93,11 +93,11 @@ func GetSettingNoCache(ctx context.Context, key string) (*Setting, error) { const contextCacheKey = "system_setting" -// GetSetting returns the setting value via the key -func GetSetting(ctx context.Context, key string) (string, error) { +// GetSettingWithCache returns the setting value via the key +func GetSettingWithCache(ctx context.Context, key string) (string, error) { return cache.GetWithContextCache(ctx, contextCacheKey, key, func() (string, error) { return cache.GetString(genSettingCacheKey(key), func() (string, error) { - res, err := GetSettingNoCache(ctx, key) + res, err := GetSetting(ctx, key) if err != nil { return "", err } @@ -110,6 +110,15 @@ func GetSetting(ctx context.Context, key string) (string, error) { // none existing keys and errors are ignored and result in false func GetSettingBool(ctx context.Context, key string) bool { s, _ := GetSetting(ctx, key) + if s == nil { + return false + } + v, _ := strconv.ParseBool(s.SettingValue) + return v +} + +func GetSettingWithCacheBool(ctx context.Context, key string) bool { + s, _ := GetSettingWithCache(ctx, key) v, _ := strconv.ParseBool(s) return v } @@ -120,7 +129,7 @@ func GetSettings(ctx context.Context, keys []string) (map[string]*Setting, error keys[i] = strings.ToLower(keys[i]) } settings := make([]*Setting, 0, len(keys)) - if err := db.GetEngine(db.DefaultContext). + if err := db.GetEngine(ctx). Where(builder.In("setting_key", keys)). Find(&settings); err != nil { return nil, err @@ -151,9 +160,9 @@ func (settings AllSettings) GetVersion(key string) int { } // GetAllSettings returns all settings from user -func GetAllSettings() (AllSettings, error) { +func GetAllSettings(ctx context.Context) (AllSettings, error) { settings := make([]*Setting, 0, 5) - if err := db.GetEngine(db.DefaultContext). + if err := db.GetEngine(ctx). Find(&settings); err != nil { return nil, err } @@ -168,12 +177,12 @@ func GetAllSettings() (AllSettings, error) { func DeleteSetting(ctx context.Context, setting *Setting) error { cache.RemoveContextData(ctx, contextCacheKey, setting.SettingKey) cache.Remove(genSettingCacheKey(setting.SettingKey)) - _, err := db.GetEngine(db.DefaultContext).Delete(setting) + _, err := db.GetEngine(ctx).Delete(setting) return err } func SetSettingNoVersion(ctx context.Context, key, value string) error { - s, err := GetSettingNoCache(ctx, key) + s, err := GetSetting(ctx, key) if IsErrSettingIsNotExist(err) { return SetSetting(ctx, &Setting{ SettingKey: key, @@ -189,7 +198,7 @@ func SetSettingNoVersion(ctx context.Context, key, value string) error { // SetSetting updates a users' setting for a specific key func SetSetting(ctx context.Context, setting *Setting) error { - if err := upsertSettingValue(strings.ToLower(setting.SettingKey), setting.SettingValue, setting.Version); err != nil { + if err := upsertSettingValue(ctx, strings.ToLower(setting.SettingKey), setting.SettingValue, setting.Version); err != nil { return err } @@ -205,8 +214,8 @@ func SetSetting(ctx context.Context, setting *Setting) error { return nil } -func upsertSettingValue(key, value string, version int) error { - return db.WithTx(db.DefaultContext, func(ctx context.Context) error { +func upsertSettingValue(parentCtx context.Context, key, value string, version int) error { + return db.WithTx(parentCtx, func(ctx context.Context) error { e := db.GetEngine(ctx) // here we use a general method to do a safe upsert for different databases (and most transaction levels) @@ -249,9 +258,9 @@ var ( LibravatarService *libravatar.Libravatar ) -func Init() error { +func Init(ctx context.Context) error { var disableGravatar bool - disableGravatarSetting, err := GetSettingNoCache(db.DefaultContext, KeyPictureDisableGravatar) + disableGravatarSetting, err := GetSetting(ctx, KeyPictureDisableGravatar) if IsErrSettingIsNotExist(err) { disableGravatar = setting_module.GetDefaultDisableGravatar() disableGravatarSetting = &Setting{SettingValue: strconv.FormatBool(disableGravatar)} @@ -262,7 +271,7 @@ func Init() error { } var enableFederatedAvatar bool - enableFederatedAvatarSetting, err := GetSettingNoCache(db.DefaultContext, KeyPictureEnableFederatedAvatar) + enableFederatedAvatarSetting, err := GetSetting(ctx, KeyPictureEnableFederatedAvatar) if IsErrSettingIsNotExist(err) { enableFederatedAvatar = setting_module.GetDefaultEnableFederatedAvatar(disableGravatar) enableFederatedAvatarSetting = &Setting{SettingValue: strconv.FormatBool(enableFederatedAvatar)} @@ -275,13 +284,13 @@ func Init() error { if setting_module.OfflineMode { disableGravatar = true enableFederatedAvatar = false - if !GetSettingBool(db.DefaultContext, KeyPictureDisableGravatar) { - if err := SetSettingNoVersion(db.DefaultContext, KeyPictureDisableGravatar, "true"); err != nil { + if !GetSettingBool(ctx, KeyPictureDisableGravatar) { + if err := SetSettingNoVersion(ctx, KeyPictureDisableGravatar, "true"); err != nil { return fmt.Errorf("Failed to set setting %q: %w", KeyPictureDisableGravatar, err) } } - if GetSettingBool(db.DefaultContext, KeyPictureEnableFederatedAvatar) { - if err := SetSettingNoVersion(db.DefaultContext, KeyPictureEnableFederatedAvatar, "false"); err != nil { + if GetSettingBool(ctx, KeyPictureEnableFederatedAvatar) { + if err := SetSettingNoVersion(ctx, KeyPictureEnableFederatedAvatar, "false"); err != nil { return fmt.Errorf("Failed to set setting %q: %w", KeyPictureEnableFederatedAvatar, err) } } diff --git a/models/system/setting_test.go b/models/system/setting_test.go index fbd04088e..e6997ad78 100644 --- a/models/system/setting_test.go +++ b/models/system/setting_test.go @@ -40,10 +40,10 @@ func TestSettings(t *testing.T) { value, err := system.GetSetting(db.DefaultContext, keyName) assert.NoError(t, err) - assert.EqualValues(t, updatedSetting.SettingValue, value) + assert.EqualValues(t, updatedSetting.SettingValue, value.SettingValue) // get all settings - settings, err = system.GetAllSettings() + settings, err = system.GetAllSettings(db.DefaultContext) assert.NoError(t, err) assert.Len(t, settings, 3) assert.EqualValues(t, updatedSetting.SettingValue, settings[strings.ToLower(updatedSetting.SettingKey)].SettingValue) @@ -51,7 +51,7 @@ func TestSettings(t *testing.T) { // delete setting err = system.DeleteSetting(db.DefaultContext, &system.Setting{SettingKey: strings.ToLower(keyName)}) assert.NoError(t, err) - settings, err = system.GetAllSettings() + settings, err = system.GetAllSettings(db.DefaultContext) assert.NoError(t, err) assert.Len(t, settings, 2) } diff --git a/models/unittest/testdb.go b/models/unittest/testdb.go index 7e327f2bd..2bc41084b 100644 --- a/models/unittest/testdb.go +++ b/models/unittest/testdb.go @@ -113,7 +113,7 @@ func MainTest(m *testing.M, testOpts *TestOptions) { if err = storage.Init(); err != nil { fatalTestError("storage.Init: %v\n", err) } - if err = system_model.Init(); err != nil { + if err = system_model.Init(db.DefaultContext); err != nil { fatalTestError("models.Init: %v\n", err) } diff --git a/models/user/avatar.go b/models/user/avatar.go index f08757b6a..72ccd090d 100644 --- a/models/user/avatar.go +++ b/models/user/avatar.go @@ -67,7 +67,7 @@ func (u *User) AvatarLinkWithSize(ctx context.Context, size int) string { useLocalAvatar := false autoGenerateAvatar := false - disableGravatar := system_model.GetSettingBool(ctx, system_model.KeyPictureDisableGravatar) + disableGravatar := system_model.GetSettingWithCacheBool(ctx, system_model.KeyPictureDisableGravatar) switch { case u.UseCustomAvatar: |