aboutsummaryrefslogtreecommitdiffstats
path: root/cache/identity_subcache.go
blob: 05a9135852efa956abf03db12b267e60a8992fd3 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
package cache

import (
	"fmt"

	"github.com/MichaelMure/git-bug/entities/identity"
	"github.com/MichaelMure/git-bug/entity"
	"github.com/MichaelMure/git-bug/repository"
)

type RepoCacheIdentity struct {
	*SubCache[*identity.Identity, *IdentityExcerpt, *IdentityCache]
}

func NewRepoCacheIdentity(repo repository.ClockedRepo,
	resolvers func() entity.Resolvers,
	getUserIdentity getUserIdentityFunc) *RepoCacheIdentity {

	makeCached := func(i *identity.Identity, entityUpdated func(id entity.Id) error) *IdentityCache {
		return NewIdentityCache(i, repo, entityUpdated)
	}

	makeIndex := func(i *IdentityCache) []string {
		// no indexing
		return nil
	}

	// TODO: this is terribly ugly, but we are currently stuck with the fact that identities are NOT using the fancy dag framework.
	//   This lead to various complication here and there to handle entities generically, and avoid large code duplication.
	//   TL;DR: something has to give, and this is the less ugly solution I found. This "normalize" identities as just another "dag framework"
	//   entity. Ideally identities would be converted to the dag framework, but right now that could lead to potential attack: if an old
	//   private key is leaked, it would be possible to craft a legal identity update that take over the most recent version. While this is
	//   meaningless in the case of a normal entity, it's really an issues for identities.

	actions := Actions[*identity.Identity]{
		ReadWithResolver: func(repo repository.ClockedRepo, resolvers entity.Resolvers, id entity.Id) (*identity.Identity, error) {
			return identity.ReadLocal(repo, id)
		},
		ReadAllWithResolver: func(repo repository.ClockedRepo, resolvers entity.Resolvers) <-chan entity.StreamedEntity[*identity.Identity] {
			return identity.ReadAllLocal(repo)
		},
		Remove:    identity.Remove,
		RemoveAll: identity.RemoveAll,
		MergeAll: func(repo repository.ClockedRepo, resolvers entity.Resolvers, remote string, mergeAuthor identity.Interface) <-chan entity.MergeResult {
			return identity.MergeAll(repo, remote)
		},
	}

	sc := NewSubCache[*identity.Identity, *IdentityExcerpt, *IdentityCache](
		repo, resolvers, getUserIdentity,
		makeCached, NewIdentityExcerpt, makeIndex, actions,
		identity.Typename, identity.Namespace,
		formatVersion, defaultMaxLoadedBugs,
	)

	return &RepoCacheIdentity{SubCache: sc}
}

// ResolveIdentityImmutableMetadata retrieve an Identity that has the exact given metadata on
// one of its version. If multiple version have the same key, the first defined take precedence.
func (c *RepoCacheIdentity) ResolveIdentityImmutableMetadata(key string, value string) (*IdentityCache, error) {
	return c.ResolveMatcher(func(excerpt *IdentityExcerpt) bool {
		return excerpt.ImmutableMetadata[key] == value
	})
}

// New create a new identity
// The new identity is written in the repository (commit)
func (c *RepoCacheIdentity) New(name string, email string) (*IdentityCache, error) {
	return c.NewRaw(name, email, "", "", nil, nil)
}

// NewFull create a new identity
// The new identity is written in the repository (commit)
func (c *RepoCacheIdentity) NewFull(name string, email string, login string, avatarUrl string, keys []*identity.Key) (*IdentityCache, error) {
	return c.NewRaw(name, email, login, avatarUrl, keys, nil)
}

func (c *RepoCacheIdentity) NewRaw(name string, email string, login string, avatarUrl string, keys []*identity.Key, metadata map[string]string) (*IdentityCache, error) {
	i, err := identity.NewIdentityFull(c.repo, name, email, login, avatarUrl, keys)
	if err != nil {
		return nil, err
	}
	return c.finishIdentity(i, metadata)
}

func (c *RepoCacheIdentity) NewFromGitUser() (*IdentityCache, error) {
	return c.NewFromGitUserRaw(nil)
}

func (c *RepoCacheIdentity) NewFromGitUserRaw(metadata map[string]string) (*IdentityCache, error) {
	i, err := identity.NewFromGitUser(c.repo)
	if err != nil {
		return nil, err
	}
	return c.finishIdentity(i, metadata)
}

func (c *RepoCacheIdentity) finishIdentity(i *identity.Identity, metadata map[string]string) (*IdentityCache, error) {
	for key, value := range metadata {
		i.SetMetadata(key, value)
	}

	err := i.Commit(c.repo)
	if err != nil {
		return nil, err
	}

	c.mu.Lock()
	if _, has := c.cached[i.Id()]; has {
		return nil, fmt.Errorf("identity %s already exist in the cache", i.Id())
	}

	cached := NewIdentityCache(i, c.repo, c.entityUpdated)
	c.cached[i.Id()] = cached
	c.mu.Unlock()

	// force the write of the excerpt
	err = c.entityUpdated(i.Id())
	if err != nil {
		return nil, err
	}

	return cached, nil
}