1package cache
2
3import (
4 "github.com/git-bug/git-bug/entities/identity"
5 "github.com/git-bug/git-bug/entity"
6 "github.com/git-bug/git-bug/repository"
7)
8
9type RepoCacheIdentity struct {
10 *SubCache[*identity.Identity, *IdentityExcerpt, *IdentityCache]
11}
12
13func NewRepoCacheIdentity(repo repository.ClockedRepo,
14 resolvers func() entity.Resolvers,
15 getUserIdentity getUserIdentityFunc) *RepoCacheIdentity {
16
17 makeCached := func(i *identity.Identity, entityUpdated func(id entity.Id) error) *IdentityCache {
18 return NewIdentityCache(i, repo, entityUpdated)
19 }
20
21 makeIndex := func(i *IdentityCache) []string {
22 // no indexing
23 return nil
24 }
25
26 // TODO: this is terribly ugly, but we are currently stuck with the fact that identities are NOT using the fancy dag framework.
27 // This lead to various complication here and there to handle entities generically, and avoid large code duplication.
28 // TL;DR: something has to give, and this is the less ugly solution I found. This "normalize" identities as just another "dag framework"
29 // entity. Ideally identities would be converted to the dag framework, but right now that could lead to potential attack: if an old
30 // private key is leaked, it would be possible to craft a legal identity update that take over the most recent version. While this is
31 // meaningless in the case of a normal entity, it's really an issues for identities.
32
33 actions := Actions[*identity.Identity]{
34 ReadWithResolver: func(repo repository.ClockedRepo, resolvers entity.Resolvers, id entity.Id) (*identity.Identity, error) {
35 return identity.ReadLocal(repo, id)
36 },
37 ReadAllWithResolver: func(repo repository.ClockedRepo, resolvers entity.Resolvers) <-chan entity.StreamedEntity[*identity.Identity] {
38 return identity.ReadAllLocal(repo)
39 },
40 Remove: identity.Remove,
41 RemoveAll: identity.RemoveAll,
42 MergeAll: func(repo repository.ClockedRepo, resolvers entity.Resolvers, remote string, mergeAuthor identity.Interface) <-chan entity.MergeResult {
43 return identity.MergeAll(repo, remote)
44 },
45 }
46
47 sc := NewSubCache[*identity.Identity, *IdentityExcerpt, *IdentityCache](
48 repo, resolvers, getUserIdentity,
49 makeCached, NewIdentityExcerpt, makeIndex, actions,
50 identity.Typename, identity.Namespace,
51 formatVersion, defaultMaxLoadedBugs,
52 )
53
54 return &RepoCacheIdentity{SubCache: sc}
55}
56
57// ResolveIdentityImmutableMetadata retrieve an Identity that has the exact given metadata on
58// one of its version. If multiple version have the same key, the first defined take precedence.
59func (c *RepoCacheIdentity) ResolveIdentityImmutableMetadata(key string, value string) (*IdentityCache, error) {
60 return c.ResolveMatcher(func(excerpt *IdentityExcerpt) bool {
61 return excerpt.ImmutableMetadata[key] == value
62 })
63}
64
65// New create a new identity
66// The new identity is written in the repository (commit)
67func (c *RepoCacheIdentity) New(name string, email string) (*IdentityCache, error) {
68 return c.NewRaw(name, email, "", "", nil, nil)
69}
70
71// NewFull create a new identity
72// The new identity is written in the repository (commit)
73func (c *RepoCacheIdentity) NewFull(name string, email string, login string, avatarUrl string, keys []*identity.Key) (*IdentityCache, error) {
74 return c.NewRaw(name, email, login, avatarUrl, keys, nil)
75}
76
77func (c *RepoCacheIdentity) NewRaw(name string, email string, login string, avatarUrl string, keys []*identity.Key, metadata map[string]string) (*IdentityCache, error) {
78 i, err := identity.NewIdentityFull(c.repo, name, email, login, avatarUrl, keys)
79 if err != nil {
80 return nil, err
81 }
82 return c.finishIdentity(i, metadata)
83}
84
85func (c *RepoCacheIdentity) NewFromGitUser() (*IdentityCache, error) {
86 return c.NewFromGitUserRaw(nil)
87}
88
89func (c *RepoCacheIdentity) NewFromGitUserRaw(metadata map[string]string) (*IdentityCache, error) {
90 i, err := identity.NewFromGitUser(c.repo)
91 if err != nil {
92 return nil, err
93 }
94 return c.finishIdentity(i, metadata)
95}
96
97func (c *RepoCacheIdentity) finishIdentity(i *identity.Identity, metadata map[string]string) (*IdentityCache, error) {
98 for key, value := range metadata {
99 i.SetMetadata(key, value)
100 }
101
102 err := i.Commit(c.repo)
103 if err != nil {
104 return nil, err
105 }
106
107 cached, err := c.add(i)
108 if err != nil {
109 return nil, err
110 }
111
112 return cached, nil
113}