summaryrefslogtreecommitdiffstats
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/auth/webauthn/webauthn.go3
-rw-r--r--modules/cache/context.go92
-rw-r--r--modules/cache/context_test.go41
-rw-r--r--modules/gitgraph/graph_models.go8
-rw-r--r--modules/repository/commits.go12
-rw-r--r--modules/repository/commits_test.go7
-rw-r--r--modules/repository/repo.go6
-rw-r--r--modules/templates/helper.go23
8 files changed, 163 insertions, 29 deletions
diff --git a/modules/auth/webauthn/webauthn.go b/modules/auth/webauthn/webauthn.go
index 937da872ca..e732878f85 100644
--- a/modules/auth/webauthn/webauthn.go
+++ b/modules/auth/webauthn/webauthn.go
@@ -8,6 +8,7 @@ import (
"encoding/gob"
"code.gitea.io/gitea/models/auth"
+ "code.gitea.io/gitea/models/db"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/setting"
@@ -62,7 +63,7 @@ func (u *User) WebAuthnDisplayName() string {
// WebAuthnIcon implements the webauthn.User interface
func (u *User) WebAuthnIcon() string {
- return (*user_model.User)(u).AvatarLink()
+ return (*user_model.User)(u).AvatarLink(db.DefaultContext)
}
// WebAuthnCredentials implementns the webauthn.User interface
diff --git a/modules/cache/context.go b/modules/cache/context.go
new file mode 100644
index 0000000000..f741a87445
--- /dev/null
+++ b/modules/cache/context.go
@@ -0,0 +1,92 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package cache
+
+import (
+ "context"
+ "sync"
+
+ "code.gitea.io/gitea/modules/log"
+)
+
+// cacheContext is a context that can be used to cache data in a request level context
+// This is useful for caching data that is expensive to calculate and is likely to be
+// used multiple times in a request.
+type cacheContext struct {
+ ctx context.Context
+ data map[any]map[any]any
+ lock sync.RWMutex
+}
+
+func (cc *cacheContext) Get(tp, key any) any {
+ cc.lock.RLock()
+ defer cc.lock.RUnlock()
+ if cc.data[tp] == nil {
+ return nil
+ }
+ return cc.data[tp][key]
+}
+
+func (cc *cacheContext) Put(tp, key, value any) {
+ cc.lock.Lock()
+ defer cc.lock.Unlock()
+ if cc.data[tp] == nil {
+ cc.data[tp] = make(map[any]any)
+ }
+ cc.data[tp][key] = value
+}
+
+func (cc *cacheContext) Delete(tp, key any) {
+ cc.lock.Lock()
+ defer cc.lock.Unlock()
+ if cc.data[tp] == nil {
+ return
+ }
+ delete(cc.data[tp], key)
+}
+
+var cacheContextKey = struct{}{}
+
+func WithCacheContext(ctx context.Context) context.Context {
+ return context.WithValue(ctx, cacheContextKey, &cacheContext{
+ ctx: ctx,
+ data: make(map[any]map[any]any),
+ })
+}
+
+func GetContextData(ctx context.Context, tp, key any) any {
+ if c, ok := ctx.Value(cacheContextKey).(*cacheContext); ok {
+ return c.Get(tp, key)
+ }
+ log.Warn("cannot get cache context when getting data: %v", ctx)
+ return nil
+}
+
+func SetContextData(ctx context.Context, tp, key, value any) {
+ if c, ok := ctx.Value(cacheContextKey).(*cacheContext); ok {
+ c.Put(tp, key, value)
+ return
+ }
+ log.Warn("cannot get cache context when setting data: %v", ctx)
+}
+
+func RemoveContextData(ctx context.Context, tp, key any) {
+ if c, ok := ctx.Value(cacheContextKey).(*cacheContext); ok {
+ c.Delete(tp, key)
+ }
+}
+
+// GetWithContextCache returns the cache value of the given key in the given context.
+func GetWithContextCache[T any](ctx context.Context, cacheGroupKey string, cacheTargetID any, f func() (T, error)) (T, error) {
+ v := GetContextData(ctx, cacheGroupKey, cacheTargetID)
+ if vv, ok := v.(T); ok {
+ return vv, nil
+ }
+ t, err := f()
+ if err != nil {
+ return t, err
+ }
+ SetContextData(ctx, cacheGroupKey, cacheTargetID, t)
+ return t, nil
+}
diff --git a/modules/cache/context_test.go b/modules/cache/context_test.go
new file mode 100644
index 0000000000..77e3ecad2c
--- /dev/null
+++ b/modules/cache/context_test.go
@@ -0,0 +1,41 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package cache
+
+import (
+ "context"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestWithCacheContext(t *testing.T) {
+ ctx := WithCacheContext(context.Background())
+
+ v := GetContextData(ctx, "empty_field", "my_config1")
+ assert.Nil(t, v)
+
+ const field = "system_setting"
+ v = GetContextData(ctx, field, "my_config1")
+ assert.Nil(t, v)
+ SetContextData(ctx, field, "my_config1", 1)
+ v = GetContextData(ctx, field, "my_config1")
+ assert.NotNil(t, v)
+ assert.EqualValues(t, 1, v.(int))
+
+ RemoveContextData(ctx, field, "my_config1")
+ RemoveContextData(ctx, field, "my_config2") // remove an non-exist key
+
+ v = GetContextData(ctx, field, "my_config1")
+ assert.Nil(t, v)
+
+ vInt, err := GetWithContextCache(ctx, field, "my_config1", func() (int, error) {
+ return 1, nil
+ })
+ assert.NoError(t, err)
+ assert.EqualValues(t, 1, vInt)
+
+ v = GetContextData(ctx, field, "my_config1")
+ assert.EqualValues(t, 1, v)
+}
diff --git a/modules/gitgraph/graph_models.go b/modules/gitgraph/graph_models.go
index 0e0fc1cd01..748f7f3075 100644
--- a/modules/gitgraph/graph_models.go
+++ b/modules/gitgraph/graph_models.go
@@ -5,6 +5,7 @@ package gitgraph
import (
"bytes"
+ "context"
"fmt"
"strings"
@@ -88,9 +89,8 @@ func (graph *Graph) AddCommit(row, column int, flowID int64, data []byte) error
// LoadAndProcessCommits will load the git.Commits for each commit in the graph,
// the associate the commit with the user author, and check the commit verification
// before finally retrieving the latest status
-func (graph *Graph) LoadAndProcessCommits(repository *repo_model.Repository, gitRepo *git.Repository) error {
+func (graph *Graph) LoadAndProcessCommits(ctx context.Context, repository *repo_model.Repository, gitRepo *git.Repository) error {
var err error
-
var ok bool
emails := map[string]*user_model.User{}
@@ -108,12 +108,12 @@ func (graph *Graph) LoadAndProcessCommits(repository *repo_model.Repository, git
if c.Commit.Author != nil {
email := c.Commit.Author.Email
if c.User, ok = emails[email]; !ok {
- c.User, _ = user_model.GetUserByEmail(email)
+ c.User, _ = user_model.GetUserByEmail(ctx, email)
emails[email] = c.User
}
}
- c.Verification = asymkey_model.ParseCommitWithSignature(c.Commit)
+ c.Verification = asymkey_model.ParseCommitWithSignature(ctx, c.Commit)
_ = asymkey_model.CalculateTrustStatus(c.Verification, repository.GetTrustModel(), func(user *user_model.User) (bool, error) {
return repo_model.IsOwnerMemberCollaborator(repository, user.ID)
diff --git a/modules/repository/commits.go b/modules/repository/commits.go
index a47f9b2dc8..96844d5b1d 100644
--- a/modules/repository/commits.go
+++ b/modules/repository/commits.go
@@ -53,7 +53,7 @@ func (pc *PushCommits) toAPIPayloadCommit(ctx context.Context, repoPath, repoLin
authorUsername := ""
author, ok := pc.emailUsers[commit.AuthorEmail]
if !ok {
- author, err = user_model.GetUserByEmail(commit.AuthorEmail)
+ author, err = user_model.GetUserByEmail(ctx, commit.AuthorEmail)
if err == nil {
authorUsername = author.Name
pc.emailUsers[commit.AuthorEmail] = author
@@ -65,7 +65,7 @@ func (pc *PushCommits) toAPIPayloadCommit(ctx context.Context, repoPath, repoLin
committerUsername := ""
committer, ok := pc.emailUsers[commit.CommitterEmail]
if !ok {
- committer, err = user_model.GetUserByEmail(commit.CommitterEmail)
+ committer, err = user_model.GetUserByEmail(ctx, commit.CommitterEmail)
if err == nil {
// TODO: check errors other than email not found.
committerUsername = committer.Name
@@ -133,7 +133,7 @@ func (pc *PushCommits) ToAPIPayloadCommits(ctx context.Context, repoPath, repoLi
// AvatarLink tries to match user in database with e-mail
// in order to show custom avatar, and falls back to general avatar link.
-func (pc *PushCommits) AvatarLink(email string) string {
+func (pc *PushCommits) AvatarLink(ctx context.Context, email string) string {
if pc.avatars == nil {
pc.avatars = make(map[string]string)
}
@@ -147,9 +147,9 @@ func (pc *PushCommits) AvatarLink(email string) string {
u, ok := pc.emailUsers[email]
if !ok {
var err error
- u, err = user_model.GetUserByEmail(email)
+ u, err = user_model.GetUserByEmail(ctx, email)
if err != nil {
- pc.avatars[email] = avatars.GenerateEmailAvatarFastLink(email, size)
+ pc.avatars[email] = avatars.GenerateEmailAvatarFastLink(ctx, email, size)
if !user_model.IsErrUserNotExist(err) {
log.Error("GetUserByEmail: %v", err)
return ""
@@ -159,7 +159,7 @@ func (pc *PushCommits) AvatarLink(email string) string {
}
}
if u != nil {
- pc.avatars[email] = u.AvatarLinkWithSize(size)
+ pc.avatars[email] = u.AvatarLinkWithSize(ctx, size)
}
return pc.avatars[email]
diff --git a/modules/repository/commits_test.go b/modules/repository/commits_test.go
index 2ae4bc73d2..2bd8de38aa 100644
--- a/modules/repository/commits_test.go
+++ b/modules/repository/commits_test.go
@@ -9,6 +9,7 @@ import (
"testing"
"time"
+ "code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
system_model "code.gitea.io/gitea/models/system"
"code.gitea.io/gitea/models/unittest"
@@ -102,7 +103,7 @@ func TestPushCommits_ToAPIPayloadCommits(t *testing.T) {
}
func enableGravatar(t *testing.T) {
- err := system_model.SetSettingNoVersion(system_model.KeyPictureDisableGravatar, "false")
+ err := system_model.SetSettingNoVersion(db.DefaultContext, system_model.KeyPictureDisableGravatar, "false")
assert.NoError(t, err)
setting.GravatarSource = "https://secure.gravatar.com/avatar"
err = system_model.Init()
@@ -136,13 +137,13 @@ func TestPushCommits_AvatarLink(t *testing.T) {
assert.Equal(t,
"https://secure.gravatar.com/avatar/ab53a2911ddf9b4817ac01ddcd3d975f?d=identicon&s=84",
- pushCommits.AvatarLink("user2@example.com"))
+ pushCommits.AvatarLink(db.DefaultContext, "user2@example.com"))
assert.Equal(t,
"https://secure.gravatar.com/avatar/"+
fmt.Sprintf("%x", md5.Sum([]byte("nonexistent@example.com")))+
"?d=identicon&s=84",
- pushCommits.AvatarLink("nonexistent@example.com"))
+ pushCommits.AvatarLink(db.DefaultContext, "nonexistent@example.com"))
}
func TestCommitToPushCommit(t *testing.T) {
diff --git a/modules/repository/repo.go b/modules/repository/repo.go
index c03e469990..a1dba8fc6a 100644
--- a/modules/repository/repo.go
+++ b/modules/repository/repo.go
@@ -318,7 +318,7 @@ func SyncReleasesWithTags(repo *repo_model.Repository, gitRepo *git.Repository)
return nil
}
- if err := PushUpdateAddTag(repo, gitRepo, tagName, sha1, refname); err != nil {
+ if err := PushUpdateAddTag(db.DefaultContext, repo, gitRepo, tagName, sha1, refname); err != nil {
return fmt.Errorf("unable to PushUpdateAddTag: %q to Repo[%d:%s/%s]: %w", tagName, repo.ID, repo.OwnerName, repo.Name, err)
}
@@ -328,7 +328,7 @@ func SyncReleasesWithTags(repo *repo_model.Repository, gitRepo *git.Repository)
}
// PushUpdateAddTag must be called for any push actions to add tag
-func PushUpdateAddTag(repo *repo_model.Repository, gitRepo *git.Repository, tagName, sha1, refname string) error {
+func PushUpdateAddTag(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, tagName, sha1, refname string) error {
tag, err := gitRepo.GetTagWithID(sha1, tagName)
if err != nil {
return fmt.Errorf("unable to GetTag: %w", err)
@@ -350,7 +350,7 @@ func PushUpdateAddTag(repo *repo_model.Repository, gitRepo *git.Repository, tagN
createdAt := time.Unix(1, 0)
if sig != nil {
- author, err = user_model.GetUserByEmail(sig.Email)
+ author, err = user_model.GetUserByEmail(ctx, sig.Email)
if err != nil && !user_model.IsErrUserNotExist(err) {
return fmt.Errorf("unable to GetUserByEmail for %q: %w", sig.Email, err)
}
diff --git a/modules/templates/helper.go b/modules/templates/helper.go
index 7afc3aa59b..8f8f565c1f 100644
--- a/modules/templates/helper.go
+++ b/modules/templates/helper.go
@@ -25,7 +25,6 @@ import (
activities_model "code.gitea.io/gitea/models/activities"
"code.gitea.io/gitea/models/avatars"
- "code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/organization"
repo_model "code.gitea.io/gitea/models/repo"
@@ -90,8 +89,8 @@ func NewFuncMap() []template.FuncMap {
"AssetVersion": func() string {
return setting.AssetVersion
},
- "DisableGravatar": func() bool {
- return system_model.GetSettingBool(system_model.KeyPictureDisableGravatar)
+ "DisableGravatar": func(ctx context.Context) bool {
+ return system_model.GetSettingBool(ctx, system_model.KeyPictureDisableGravatar)
},
"DefaultShowFullName": func() bool {
return setting.UI.DefaultShowFullName
@@ -613,22 +612,22 @@ func AvatarHTML(src string, size int, class, name string) template.HTML {
}
// Avatar renders user avatars. args: user, size (int), class (string)
-func Avatar(item interface{}, others ...interface{}) template.HTML {
+func Avatar(ctx context.Context, item interface{}, others ...interface{}) template.HTML {
size, class := gitea_html.ParseSizeAndClass(avatars.DefaultAvatarPixelSize, avatars.DefaultAvatarClass, others...)
switch t := item.(type) {
case *user_model.User:
- src := t.AvatarLinkWithSize(size * setting.Avatar.RenderedSizeFactor)
+ src := t.AvatarLinkWithSize(ctx, size*setting.Avatar.RenderedSizeFactor)
if src != "" {
return AvatarHTML(src, size, class, t.DisplayName())
}
case *repo_model.Collaborator:
- src := t.AvatarLinkWithSize(size * setting.Avatar.RenderedSizeFactor)
+ src := t.AvatarLinkWithSize(ctx, size*setting.Avatar.RenderedSizeFactor)
if src != "" {
return AvatarHTML(src, size, class, t.DisplayName())
}
case *organization.Organization:
- src := t.AsUser().AvatarLinkWithSize(size * setting.Avatar.RenderedSizeFactor)
+ src := t.AsUser().AvatarLinkWithSize(ctx, size*setting.Avatar.RenderedSizeFactor)
if src != "" {
return AvatarHTML(src, size, class, t.AsUser().DisplayName())
}
@@ -638,9 +637,9 @@ func Avatar(item interface{}, others ...interface{}) template.HTML {
}
// AvatarByAction renders user avatars from action. args: action, size (int), class (string)
-func AvatarByAction(action *activities_model.Action, others ...interface{}) template.HTML {
- action.LoadActUser(db.DefaultContext)
- return Avatar(action.ActUser, others...)
+func AvatarByAction(ctx context.Context, action *activities_model.Action, others ...interface{}) template.HTML {
+ action.LoadActUser(ctx)
+ return Avatar(ctx, action.ActUser, others...)
}
// RepoAvatar renders repo avatars. args: repo, size(int), class (string)
@@ -655,9 +654,9 @@ func RepoAvatar(repo *repo_model.Repository, others ...interface{}) template.HTM
}
// AvatarByEmail renders avatars by email address. args: email, name, size (int), class (string)
-func AvatarByEmail(email, name string, others ...interface{}) template.HTML {
+func AvatarByEmail(ctx context.Context, email, name string, others ...interface{}) template.HTML {
size, class := gitea_html.ParseSizeAndClass(avatars.DefaultAvatarPixelSize, avatars.DefaultAvatarClass, others...)
- src := avatars.GenerateEmailAvatarFastLink(email, size*setting.Avatar.RenderedSizeFactor)
+ src := avatars.GenerateEmailAvatarFastLink(ctx, email, size*setting.Avatar.RenderedSizeFactor)
if src != "" {
return AvatarHTML(src, size, class, name)