diff options
30 files changed, 313 insertions, 155 deletions
diff --git a/bridge/core/auth/credential.go b/bridge/core/auth/credential.go index e843ede7..c1255aa6 100644 --- a/bridge/core/auth/credential.go +++ b/bridge/core/auth/credential.go @@ -40,7 +40,10 @@ type Credential interface { Kind() CredentialKind CreateTime() time.Time Validate() error + Metadata() map[string]string + GetMetadata(key string) (string, bool) + SetMetadata(key string, value string) // Return all the specific properties of the credential that need to be saved into the configuration. // This does not include Target, Kind, CreateTime and Metadata. @@ -124,6 +127,9 @@ func metaFromConfig(configs map[string]string) map[string]string { result[key] = val } } + if len(result) == 0 { + return nil + } return result } diff --git a/bridge/core/auth/credential_test.go b/bridge/core/auth/credential_test.go index 49c138cf..2f8806c9 100644 --- a/bridge/core/auth/credential_test.go +++ b/bridge/core/auth/credential_test.go @@ -63,7 +63,7 @@ func TestCredential(t *testing.T) { // Metadata - token4.Metadata()["key"] = "value" + token4.SetMetadata("key", "value") err = Store(repo, token4) assert.NoError(t, err) diff --git a/bridge/core/auth/options.go b/bridge/core/auth/options.go index 0c780dc1..74189874 100644 --- a/bridge/core/auth/options.go +++ b/bridge/core/auth/options.go @@ -26,7 +26,7 @@ func (opts *options) Match(cred Credential) bool { } for key, val := range opts.meta { - if v, ok := cred.Metadata()[key]; !ok || v != val { + if v, ok := cred.GetMetadata(key); !ok || v != val { return false } } diff --git a/bridge/core/auth/token.go b/bridge/core/auth/token.go index 60137cd9..42f960bf 100644 --- a/bridge/core/auth/token.go +++ b/bridge/core/auth/token.go @@ -30,7 +30,6 @@ func NewToken(value, target string) *Token { target: target, createTime: time.Now(), Value: value, - meta: make(map[string]string), } } @@ -88,6 +87,18 @@ func (t *Token) Metadata() map[string]string { return t.meta } +func (t *Token) GetMetadata(key string) (string, bool) { + val, ok := t.meta[key] + return val, ok +} + +func (t *Token) SetMetadata(key string, value string) { + if t.meta == nil { + t.meta = make(map[string]string) + } + t.meta[key] = value +} + func (t *Token) toConfig() map[string]string { return map[string]string{ tokenValueKey: t.Value, diff --git a/bridge/core/config.go b/bridge/core/config.go index 9a8bc959..adee5f08 100644 --- a/bridge/core/config.go +++ b/bridge/core/config.go @@ -1 +1,43 @@ package core + +import ( + "github.com/MichaelMure/git-bug/cache" + "github.com/MichaelMure/git-bug/identity" +) + +func FinishConfig(repo *cache.RepoCache, metaKey string, login string) error { + // if no user exist with the given login metadata + _, err := repo.ResolveIdentityImmutableMetadata(metaKey, login) + if err != nil && err != identity.ErrIdentityNotExist { + // real error + return err + } + if err == nil { + // found an already valid user, all good + return nil + } + + // if a default user exist, tag it with the login + user, err := repo.GetUserIdentity() + if err != nil && err != identity.ErrIdentityNotExist { + // real error + return err + } + if err == nil { + // found one + user.SetMetadata(metaKey, login) + return user.CommitAsNeeded() + } + + // otherwise create a user with that metadata + i, err := repo.NewIdentityFromGitUserRaw(map[string]string{ + metaKey: login, + }) + + err = repo.SetUserIdentity(i) + if err != nil { + return err + } + + return nil +} diff --git a/bridge/github/config.go b/bridge/github/config.go index 40653afa..9ede72d4 100644 --- a/bridge/github/config.go +++ b/bridge/github/config.go @@ -22,7 +22,6 @@ import ( "github.com/MichaelMure/git-bug/bridge/core" "github.com/MichaelMure/git-bug/bridge/core/auth" "github.com/MichaelMure/git-bug/cache" - "github.com/MichaelMure/git-bug/identity" "github.com/MichaelMure/git-bug/input" "github.com/MichaelMure/git-bug/repository" "github.com/MichaelMure/git-bug/util/colors" @@ -109,7 +108,7 @@ func (g *Github) Configure(repo *cache.RepoCache, params core.BridgeParams) (cor } case params.TokenRaw != "": cred = auth.NewToken(params.TokenRaw, target) - cred.Metadata()[auth.MetaKeyLogin] = login + cred.SetMetadata(auth.MetaKeyLogin, login) default: cred, err = promptTokenOptions(repo, login, owner, project) if err != nil { @@ -140,34 +139,6 @@ func (g *Github) Configure(repo *cache.RepoCache, params core.BridgeParams) (cor return nil, err } - // TODO - func(login string) error { - // if no user exist with the given login - _, err := repo.ResolveIdentityLogin(login) - if err != nil && err != identity.ErrIdentityNotExist { - return err - } - - // tag the default user with the github login, if any - user, err := repo.GetUserIdentity() - if err == identity.ErrNoIdentitySet { - return nil - } - if err != nil { - return err - } - - userLogin, ok := user.ImmutableMetadata()[metaKeyGithubLogin] - if !ok { - user.SetMetadata() - } - - }(login) - - // Todo: if no user exist with the given login - // - tag the default user with the github login - // - add a command to manually tag a user ? - // don't forget to store the now known valid token if !auth.IdExist(repo, cred.ID()) { err = auth.Store(repo, cred) @@ -176,7 +147,7 @@ func (g *Github) Configure(repo *cache.RepoCache, params core.BridgeParams) (cor } } - return conf, nil + return conf, core.FinishConfig(repo, metaKeyGithubLogin, login) } func (*Github) ValidateConfig(conf core.Configuration) error { @@ -318,7 +289,7 @@ func promptTokenOptions(repo repository.RepoConfig, login, owner, project string return nil, err } token := auth.NewToken(value, target) - token.Metadata()[auth.MetaKeyLogin] = login + token.SetMetadata(auth.MetaKeyLogin, login) return token, nil case 2: value, err := loginAndRequestToken(login, owner, project) @@ -326,7 +297,7 @@ func promptTokenOptions(repo repository.RepoConfig, login, owner, project string return nil, err } token := auth.NewToken(value, target) - token.Metadata()[auth.MetaKeyLogin] = login + token.SetMetadata(auth.MetaKeyLogin, login) return token, nil default: return creds[index-3], nil diff --git a/bridge/github/export.go b/bridge/github/export.go index 1cc66dee..663361f5 100644 --- a/bridge/github/export.go +++ b/bridge/github/export.go @@ -21,7 +21,6 @@ import ( "github.com/MichaelMure/git-bug/cache" "github.com/MichaelMure/git-bug/entity" "github.com/MichaelMure/git-bug/identity" - "github.com/MichaelMure/git-bug/repository" ) var ( @@ -35,6 +34,13 @@ type githubExporter struct { // cache identities clients identityClient map[entity.Id]*githubv4.Client + // the client to use for non user-specific queries + // should be the client of the default user + defaultClient *githubv4.Client + + // the token of the default user + defaultToken *auth.Token + // github repository ID repositoryID string @@ -53,12 +59,34 @@ func (ge *githubExporter) Init(repo *cache.RepoCache, conf core.Configuration) e ge.cachedOperationIDs = make(map[entity.Id]string) ge.cachedLabels = make(map[string]string) + user, err := repo.GetUserIdentity() + if err != nil { + return err + } + // preload all clients - err := ge.cacheAllClient(repo) + err = ge.cacheAllClient(repo) if err != nil { return err } + ge.defaultClient, err = ge.getClientForIdentity(user.Id()) + if err != nil { + return err + } + + login := user.ImmutableMetadata()[metaKeyGithubLogin] + creds, err := auth.List(repo, auth.WithMeta(metaKeyGithubLogin, login), auth.WithTarget(target), auth.WithKind(auth.KindToken)) + if err != nil { + return err + } + + if len(creds) == 0 { + return ErrMissingIdentityToken + } + + ge.defaultToken = creds[0].(*auth.Token) + return nil } @@ -69,7 +97,7 @@ func (ge *githubExporter) cacheAllClient(repo *cache.RepoCache) error { } for _, cred := range creds { - login, ok := cred.Metadata()[auth.MetaKeyLogin] + login, ok := cred.GetMetadata(auth.MetaKeyLogin) if !ok { _, _ = fmt.Fprintf(os.Stderr, "credential %s is not tagged with Github login\n", cred.ID().Human()) continue @@ -80,9 +108,9 @@ func (ge *githubExporter) cacheAllClient(repo *cache.RepoCache) error { continue } - if _, ok := ge.identityClient[cred.UserId()]; !ok { + if _, ok := ge.identityClient[user.Id()]; !ok { client := buildClient(creds[0].(*auth.Token)) - ge.identityClient[cred.UserId()] = client + ge.identityClient[user.Id()] = client } } @@ -462,11 +490,12 @@ func (ge *githubExporter) cacheGithubLabels(ctx context.Context, gc *githubv4.Cl for hasNextPage { // create a new timeout context at each iteration ctx, cancel := context.WithTimeout(ctx, defaultTimeout) - defer cancel() if err := gc.Query(ctx, &q, variables); err != nil { + cancel() return err } + cancel() for _, label := range q.Repository.Labels.Nodes { ge.cachedLabels[label.Name] = label.ID diff --git a/bridge/github/export_test.go b/bridge/github/export_test.go index 5a0bc653..d2cfb1f9 100644 --- a/bridge/github/export_test.go +++ b/bridge/github/export_test.go @@ -176,7 +176,7 @@ func TestPushPull(t *testing.T) { return deleteRepository(projectName, envUser, envToken) }) - token := auth.NewToken(author.Id(), envToken, target) + token := auth.NewToken(envToken, target) err = auth.Store(repo, token) require.NoError(t, err) diff --git a/bridge/github/import.go b/bridge/github/import.go index aac4f119..f2c9a53d 100644 --- a/bridge/github/import.go +++ b/bridge/github/import.go @@ -543,7 +543,6 @@ func (gi *githubImporter) ensurePerson(repo *cache.RepoCache, actor *actor) (*ca i, err = repo.NewIdentityRaw( name, email, - string(actor.Login), string(actor.AvatarUrl), map[string]string{ metaKeyGithubLogin: string(actor.Login), @@ -590,7 +589,6 @@ func (gi *githubImporter) getGhost(repo *cache.RepoCache) (*cache.IdentityCache, return repo.NewIdentityRaw( name, "", - string(q.User.Login), string(q.User.AvatarUrl), map[string]string{ metaKeyGithubLogin: string(q.User.Login), diff --git a/bridge/launchpad/import.go b/bridge/launchpad/import.go index 619631b3..ecbf74f8 100644 --- a/bridge/launchpad/import.go +++ b/bridge/launchpad/import.go @@ -38,7 +38,6 @@ func (li *launchpadImporter) ensurePerson(repo *cache.RepoCache, owner LPPerson) return repo.NewIdentityRaw( owner.Name, "", - owner.Login, "", map[string]string{ metaKeyLaunchpadLogin: owner.Login, diff --git a/bug/status.go b/bug/status.go index 737c8d31..9e998034 100644 --- a/bug/status.go +++ b/bug/status.go @@ -44,7 +44,7 @@ func StatusFromString(str string) (Status, error) { case "closed": return ClosedStatus, nil default: - return 0, fmt.Errorf("unknow status") + return 0, fmt.Errorf("unknown status") } } diff --git a/cache/bug_excerpt.go b/cache/bug_excerpt.go index 36c7dcfe..10e522f9 100644 --- a/cache/bug_excerpt.go +++ b/cache/bug_excerpt.go @@ -2,7 +2,6 @@ package cache import ( "encoding/gob" - "fmt" "github.com/MichaelMure/git-bug/bug" "github.com/MichaelMure/git-bug/entity" @@ -43,21 +42,11 @@ type BugExcerpt struct { // identity.Bare data are directly embedded in the bug excerpt type LegacyAuthorExcerpt struct { - Name string - Login string + Name string } func (l LegacyAuthorExcerpt) DisplayName() string { - switch { - case l.Name == "" && l.Login != "": - return l.Login - case l.Name != "" && l.Login == "": - return l.Name - case l.Name != "" && l.Login != "": - return fmt.Sprintf("%s (%s)", l.Name, l.Login) - } - - panic("invalid person data") + return l.Name } func NewBugExcerpt(b bug.Interface, snap *bug.Snapshot) *BugExcerpt { @@ -95,8 +84,7 @@ func NewBugExcerpt(b bug.Interface, snap *bug.Snapshot) *BugExcerpt { e.AuthorId = snap.Author.Id() case *identity.Bare: e.LegacyAuthor = LegacyAuthorExcerpt{ - Login: snap.Author.Login(), - Name: snap.Author.Name(), + Name: snap.Author.Name(), } default: panic("unhandled identity type") diff --git a/cache/filter.go b/cache/filter.go index 27e92cf3..9b1de1d5 100644 --- a/cache/filter.go +++ b/cache/filter.go @@ -37,8 +37,7 @@ func AuthorFilter(query string) Filter { } // Legacy identity support - return strings.Contains(strings.ToLower(excerpt.LegacyAuthor.Name), query) || - strings.Contains(strings.ToLower(excerpt.LegacyAuthor.Login), query) + return strings.Contains(strings.ToLower(excerpt.LegacyAuthor.Name), query) } } diff --git a/cache/identity_cache.go b/cache/identity_cache.go index e2129f84..eb5ee183 100644 --- a/cache/identity_cache.go +++ b/cache/identity_cache.go @@ -21,7 +21,7 @@ func (i *IdentityCache) notifyUpdated() error { return i.repoCache.identityUpdated(i.Identity.Id()) } -func (i *IdentityCache) Mutate(f func(identity.IdentityMutator) identity.IdentityMutator) error { +func (i *IdentityCache) Mutate(f func(identity.Mutator) identity.Mutator) error { i.Identity.Mutate(f) return i.notifyUpdated() } diff --git a/cache/identity_excerpt.go b/cache/identity_excerpt.go index 18514e9a..06788aa5 100644 --- a/cache/identity_excerpt.go +++ b/cache/identity_excerpt.go @@ -2,7 +2,6 @@ package cache import ( "encoding/gob" - "fmt" "strings" "github.com/MichaelMure/git-bug/entity" @@ -21,7 +20,6 @@ type IdentityExcerpt struct { Id entity.Id Name string - Login string ImmutableMetadata map[string]string } @@ -29,7 +27,6 @@ func NewIdentityExcerpt(i *identity.Identity) *IdentityExcerpt { return &IdentityExcerpt{ Id: i.Id(), Name: i.Name(), - Login: i.Login(), ImmutableMetadata: i.ImmutableMetadata(), } } @@ -37,23 +34,13 @@ func NewIdentityExcerpt(i *identity.Identity) *IdentityExcerpt { // DisplayName return a non-empty string to display, representing the // identity, based on the non-empty values. func (i *IdentityExcerpt) DisplayName() string { - switch { - case i.Name == "" && i.Login != "": - return i.Login - case i.Name != "" && i.Login == "": - return i.Name - case i.Name != "" && i.Login != "": - return fmt.Sprintf("%s (%s)", i.Name, i.Login) - } - - panic("invalid person data") + return i.Name } // Match matches a query with the identity name, login and ID prefixes func (i *IdentityExcerpt) Match(query string) bool { return i.Id.HasPrefix(query) || - strings.Contains(strings.ToLower(i.Name), query) || - strings.Contains(strings.ToLower(i.Login), query) + strings.Contains(strings.ToLower(i.Name), query) } /* diff --git a/cache/query.go b/cache/query.go index 633ef1c2..967c18d6 100644 --- a/cache/query.go +++ b/cache/query.go @@ -91,7 +91,7 @@ func ParseQuery(query string) (*Query, error) { sortingDone = true default: - return nil, fmt.Errorf("unknow qualifier name %s", qualifierName) + return nil, fmt.Errorf("unknown qualifier name %s", qualifierName) } } @@ -165,7 +165,7 @@ func (q *Query) parseSorting(query string) error { q.OrderDirection = OrderAscending default: - return fmt.Errorf("unknow sorting %s", query) + return fmt.Errorf("unknown sorting %s", query) } return nil diff --git a/cache/repo_cache.go b/cache/repo_cache.go index 99afeb41..18be9b5a 100644 --- a/cache/repo_cache.go +++ b/cache/repo_cache.go @@ -789,12 +789,6 @@ func (c *RepoCache) ResolveIdentityImmutableMetadata(key string, value string) ( }) } -func (c *RepoCache) ResolveIdentityLogin(login string) (*IdentityCache, error) { - return c.ResolveIdentityMatcher(func(excerpt *IdentityExcerpt) bool { - return excerpt.Login == login - }) -} - func (c *RepoCache) ResolveIdentityMatcher(f func(*IdentityExcerpt) bool) (*IdentityCache, error) { // preallocate but empty matching := make([]entity.Id, 0, 5) @@ -869,21 +863,36 @@ func (c *RepoCache) IsUserIdentitySet() (bool, error) { return identity.IsUserIdentitySet(c.repo) } +func (c *RepoCache) NewIdentityFromGitUser() (*IdentityCache, error) { + return c.NewIdentityFromGitUserRaw(nil) +} + +func (c *RepoCache) NewIdentityFromGitUserRaw(metadata map[string]string) (*IdentityCache, error) { + i, err := identity.NewFromGitUser(c.repo) + if err != nil { + return nil, err + } + return c.finishIdentity(i, metadata) +} + // NewIdentity create a new identity // The new identity is written in the repository (commit) func (c *RepoCache) NewIdentity(name string, email string) (*IdentityCache, error) { - return c.NewIdentityRaw(name, email, "", "", nil) + return c.NewIdentityRaw(name, email, "", nil) } // NewIdentityFull create a new identity // The new identity is written in the repository (commit) -func (c *RepoCache) NewIdentityFull(name string, email string, login string, avatarUrl string) (*IdentityCache, error) { - return c.NewIdentityRaw(name, email, login, avatarUrl, nil) +func (c *RepoCache) NewIdentityFull(name string, email string, avatarUrl string) (*IdentityCache, error) { + return c.NewIdentityRaw(name, email, avatarUrl, nil) } -func (c *RepoCache) NewIdentityRaw(name string, email string, login string, avatarUrl string, metadata map[string]string) (*IdentityCache, error) { - i := identity.NewIdentityFull(name, email, login, avatarUrl) +func (c *RepoCache) NewIdentityRaw(name string, email string, avatarUrl string, metadata map[string]string) (*IdentityCache, error) { + i := identity.NewIdentityFull(name, email, avatarUrl) + return c.finishIdentity(i, metadata) +} +func (c *RepoCache) finishIdentity(i *identity.Identity, metadata map[string]string) (*IdentityCache, error) { for key, value := range metadata { i.SetMetadata(key, value) } diff --git a/graphql/graph/gen_graph.go b/graphql/graph/gen_graph.go index 215603cb..ee170ecc 100644 --- a/graphql/graph/gen_graph.go +++ b/graphql/graph/gen_graph.go @@ -210,7 +210,6 @@ type ComplexityRoot struct { HumanID func(childComplexity int) int ID func(childComplexity int) int IsProtected func(childComplexity int) int - Login func(childComplexity int) int Name func(childComplexity int) int } @@ -1139,13 +1138,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Identity.IsProtected(childComplexity), true - case "Identity.login": - if e.complexity.Identity.Login == nil { - break - } - - return e.complexity.Identity.Login(childComplexity), true - case "Identity.name": if e.complexity.Identity.Name == nil { break @@ -2070,6 +2062,7 @@ type BugConnection { An edge in a connection. """ type BugEdge { +<<<<<<< HEAD """ A cursor for use in pagination. """ @@ -2078,6 +2071,105 @@ type BugEdge { The item at the end of the edge. """ node: Bug! +======= + """A cursor for use in pagination.""" + cursor: String! + """The item at the end of the edge.""" + node: Bug! +} +`}, + &ast.Source{Name: "schema/identity.graphql", Input: `"""Represents an identity""" +type Identity { + """The identifier for this identity""" + id: String! + """The human version (truncated) identifier for this identity""" + humanId: String! + """The name of the person, if known.""" + name: String + """The email of the person, if known.""" + email: String + """A non-empty string to display, representing the identity, based on the non-empty values.""" + displayName: String! + """An url to an avatar""" + avatarUrl: String + """isProtected is true if the chain of git commits started to be signed. + If that's the case, only signed commit with a valid key for this identity can be added.""" + isProtected: Boolean! +} + +type IdentityConnection { + edges: [IdentityEdge!]! + nodes: [Identity!]! + pageInfo: PageInfo! + totalCount: Int! +} + +type IdentityEdge { + cursor: String! + node: Identity! +}`}, + &ast.Source{Name: "schema/label.graphql", Input: `"""Label for a bug.""" +type Label { + """The name of the label.""" + name: String! + """Color of the label.""" + color: Color! +} + +type LabelConnection { + edges: [LabelEdge!]! + nodes: [Label!]! + pageInfo: PageInfo! + totalCount: Int! +} + +type LabelEdge { + cursor: String! + node: Label! +}`}, + &ast.Source{Name: "schema/mutations.graphql", Input: `input NewBugInput { + """A unique identifier for the client performing the mutation.""" + clientMutationId: String + """"The name of the repository. If not set, the default repository is used.""" + repoRef: String + """The title of the new bug.""" + title: String! + """The first message of the new bug.""" + message: String! + """The collection of file's hash required for the first message.""" + files: [Hash!] +} + +type NewBugPayload { + """A unique identifier for the client performing the mutation.""" + clientMutationId: String + """The created bug.""" + bug: Bug! + """The resulting operation.""" + operation: CreateOperation! +} + +input AddCommentInput { + """A unique identifier for the client performing the mutation.""" + clientMutationId: String + """"The name of the repository. If not set, the default repository is used.""" + repoRef: String + """The bug ID's prefix.""" + prefix: String! + """The first message of the new bug.""" + message: String! + """The collection of file's hash required for the first message.""" + files: [Hash!] +} + +type AddCommentPayload { + """A unique identifier for the client performing the mutation.""" + clientMutationId: String + """The affected bug.""" + bug: Bug! + """The resulting operation.""" + operation: AddCommentOperation! +>>>>>>> more more wip } input ChangeLabelInput { """ @@ -6215,6 +6307,7 @@ func (ec *executionContext) _Identity_email(ctx context.Context, field graphql.C return ec.marshalOString2string(ctx, field.Selections, res) } +<<<<<<< HEAD func (ec *executionContext) _Identity_login(ctx context.Context, field graphql.CollectedField, obj identity.Interface) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { @@ -6247,6 +6340,10 @@ func (ec *executionContext) _Identity_login(ctx context.Context, field graphql.C } func (ec *executionContext) _Identity_displayName(ctx context.Context, field graphql.CollectedField, obj identity.Interface) (ret graphql.Marshaler) { +======= +func (ec *executionContext) _Identity_displayName(ctx context.Context, field graphql.CollectedField, obj *identity.Interface) (ret graphql.Marshaler) { + ctx = ec.Tracer.StartFieldExecution(ctx, field) +>>>>>>> more more wip defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -11945,9 +12042,22 @@ func (ec *executionContext) _Identity(ctx context.Context, sel ast.SelectionSet, case "name": out.Values[i] = ec._Identity_name(ctx, field, obj) case "email": +<<<<<<< HEAD out.Values[i] = ec._Identity_email(ctx, field, obj) case "login": out.Values[i] = ec._Identity_login(ctx, field, obj) +======= + field := field + out.Concurrently(i, func() (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Identity_email(ctx, field, obj) + return res + }) +>>>>>>> more more wip case "displayName": out.Values[i] = ec._Identity_displayName(ctx, field, obj) if out.Values[i] == graphql.Null { diff --git a/graphql/resolvers/identity.go b/graphql/resolvers/identity.go index da8e7b08..2d1e909b 100644 --- a/graphql/resolvers/identity.go +++ b/graphql/resolvers/identity.go @@ -14,7 +14,3 @@ type identityResolver struct{} func (identityResolver) ID(ctx context.Context, obj identity.Interface) (string, error) { return obj.Id().String(), nil } - -func (identityResolver) HumanID(ctx context.Context, obj identity.Interface) (string, error) { - return obj.Id().Human(), nil -} diff --git a/graphql/schema/identity.graphql b/graphql/schema/identity.graphql index 6872ecb9..6490d538 100644 --- a/graphql/schema/identity.graphql +++ b/graphql/schema/identity.graphql @@ -8,9 +8,7 @@ type Identity { name: String """The email of the person, if known.""" email: String - """The login of the person, if known.""" - login: String - """A string containing the either the name of the person, its login or both""" + """A non-empty string to display, representing the identity, based on the non-empty values.""" displayName: String! """An url to an avatar""" avatarUrl: String diff --git a/graphql/schema/root.graphql b/graphql/schema/root.graphql index f66272ca..2a12cc37 100644 --- a/graphql/schema/root.graphql +++ b/graphql/schema/root.graphql @@ -3,6 +3,8 @@ type Query { defaultRepository: Repository """Access a repository by reference/name.""" repository(ref: String!): Repository + + #TODO: connection for all repositories } type Mutation { diff --git a/identity/bare.go b/identity/bare.go index 26ecdf03..a02ec790 100644 --- a/identity/bare.go +++ b/identity/bare.go @@ -112,13 +112,13 @@ func (i *Bare) AvatarUrl() string { } // Keys return the last version of the valid keys -func (i *Bare) Keys() []Key { - return []Key{} +func (i *Bare) Keys() []*Key { + return nil } // ValidKeysAtTime return the set of keys valid at a given lamport time -func (i *Bare) ValidKeysAtTime(time lamport.Time) []Key { - return []Key{} +func (i *Bare) ValidKeysAtTime(_ lamport.Time) []*Key { + return nil } // DisplayName return a non-empty string to display, representing the diff --git a/identity/identity.go b/identity/identity.go index 655afd31..c33a8818 100644 --- a/identity/identity.go +++ b/identity/identity.go @@ -275,7 +275,7 @@ type Mutator struct { Name string Email string AvatarUrl string - Keys []Key + Keys []*Key } // Mutate allow to create a new version of the Identity @@ -507,13 +507,13 @@ func (i *Identity) AvatarUrl() string { } // Keys return the last version of the valid keys -func (i *Identity) Keys() []Key { +func (i *Identity) Keys() []*Key { return i.lastVersion().keys } // ValidKeysAtTime return the set of keys valid at a given lamport time -func (i *Identity) ValidKeysAtTime(time lamport.Time) []Key { - var result []Key +func (i *Identity) ValidKeysAtTime(time lamport.Time) []*Key { + var result []*Key for _, v := range i.versions { if v.time > time { @@ -550,11 +550,11 @@ func (i *Identity) LastModification() timestamp.Timestamp { } // SetMetadata store arbitrary metadata along the last not-commit Version. -// If the Version has been commit to git already, a new version is added and will need to be +// If the Version has been commit to git already, a new identical version is added and will need to be // commit. func (i *Identity) SetMetadata(key string, value string) { if i.lastVersion().commitHash != "" { - + i.versions = append(i.versions, i.lastVersion().Clone()) } i.lastVersion().SetMetadata(key, value) } @@ -588,3 +588,9 @@ func (i *Identity) MutableMetadata() map[string]string { return metadata } + +// addVersionForTest add a new version to the identity +// Only for testing ! +func (i *Identity) addVersionForTest(version *Version) { + i.versions = append(i.versions, version) +} diff --git a/identity/identity_actions_test.go b/identity/identity_actions_test.go index 142ffaa6..713b3246 100644 --- a/identity/identity_actions_test.go +++ b/identity/identity_actions_test.go @@ -48,14 +48,14 @@ func TestPushPull(t *testing.T) { // Update both - identity1.AddVersion(&Version{ + identity1.addVersionForTest(&Version{ name: "name1b", email: "email1b", }) err = identity1.Commit(repoA) require.NoError(t, err) - identity2.AddVersion(&Version{ + identity2.addVersionForTest(&Version{ name: "name2b", email: "email2b", }) @@ -92,7 +92,7 @@ func TestPushPull(t *testing.T) { // Concurrent update - identity1.AddVersion(&Version{ + identity1.addVersionForTest(&Version{ name: "name1c", email: "email1c", }) @@ -102,7 +102,7 @@ func TestPushPull(t *testing.T) { identity1B, err := ReadLocal(repoB, identity1.Id()) require.NoError(t, err) - identity1B.AddVersion(&Version{ + identity1B.addVersionForTest(&Version{ name: "name1concurrent", email: "email1concurrent", }) diff --git a/identity/identity_stub.go b/identity/identity_stub.go index be52ffc0..7e2fcd94 100644 --- a/identity/identity_stub.go +++ b/identity/identity_stub.go @@ -64,11 +64,11 @@ func (IdentityStub) AvatarUrl() string { panic("identities needs to be properly loaded with identity.ReadLocal()") } -func (IdentityStub) Keys() []Key { +func (IdentityStub) Keys() []*Key { panic("identities needs to be properly loaded with identity.ReadLocal()") } -func (IdentityStub) ValidKeysAtTime(time lamport.Time) []Key { +func (IdentityStub) ValidKeysAtTime(_ lamport.Time) []*Key { panic("identities needs to be properly loaded with identity.ReadLocal()") } diff --git a/identity/identity_test.go b/identity/identity_test.go index f91c548f..ee6ccdf7 100644 --- a/identity/identity_test.go +++ b/identity/identity_test.go @@ -44,7 +44,7 @@ func TestIdentityCommitLoad(t *testing.T) { time: 100, name: "René Descartes", email: "rene.descartes@example.com", - keys: []Key{ + keys: []*Key{ {PubKey: "pubkeyA"}, }, }, @@ -52,7 +52,7 @@ func TestIdentityCommitLoad(t *testing.T) { time: 200, name: "René Descartes", email: "rene.descartes@example.com", - keys: []Key{ + keys: []*Key{ {PubKey: "pubkeyB"}, }, }, @@ -60,7 +60,7 @@ func TestIdentityCommitLoad(t *testing.T) { time: 201, name: "René Descartes", email: "rene.descartes@example.com", - keys: []Key{ + keys: []*Key{ {PubKey: "pubkeyC"}, }, }, @@ -79,20 +79,25 @@ func TestIdentityCommitLoad(t *testing.T) { // add more version - identity.AddVersion(&Version{ + identity.Mutate(func(orig Mutator) Mutator { + + return orig + }) + + identity.addVersionForTest(&Version{ time: 201, name: "René Descartes", email: "rene.descartes@example.com", - keys: []Key{ + keys: []*Key{ {PubKey: "pubkeyD"}, }, }) - identity.AddVersion(&Version{ + identity.addVersionForTest(&Version{ time: 300, name: "René Descartes", email: "rene.descartes@example.com", - keys: []Key{ + keys: []*Key{ {PubKey: "pubkeyE"}, }, }) @@ -123,7 +128,7 @@ func TestIdentity_ValidKeysAtTime(t *testing.T) { time: 100, name: "René Descartes", email: "rene.descartes@example.com", - keys: []Key{ + keys: []*Key{ {PubKey: "pubkeyA"}, }, }, @@ -131,7 +136,7 @@ func TestIdentity_ValidKeysAtTime(t *testing.T) { time: 200, name: "René Descartes", email: "rene.descartes@example.com", - keys: []Key{ + keys: []*Key{ {PubKey: "pubkeyB"}, }, }, @@ -139,7 +144,7 @@ func TestIdentity_ValidKeysAtTime(t *testing.T) { time: 201, name: "René Descartes", email: "rene.descartes@example.com", - keys: []Key{ + keys: []*Key{ {PubKey: "pubkeyC"}, }, }, @@ -147,7 +152,7 @@ func TestIdentity_ValidKeysAtTime(t *testing.T) { time: 201, name: "René Descartes", email: "rene.descartes@example.com", - keys: []Key{ + keys: []*Key{ {PubKey: "pubkeyD"}, }, }, @@ -155,7 +160,7 @@ func TestIdentity_ValidKeysAtTime(t *testing.T) { time: 300, name: "René Descartes", email: "rene.descartes@example.com", - keys: []Key{ + keys: []*Key{ {PubKey: "pubkeyE"}, }, }, @@ -163,13 +168,13 @@ func TestIdentity_ValidKeysAtTime(t *testing.T) { } assert.Nil(t, identity.ValidKeysAtTime(10)) - assert.Equal(t, identity.ValidKeysAtTime(100), []Key{{PubKey: "pubkeyA"}}) - assert.Equal(t, identity.ValidKeysAtTime(140), []Key{{PubKey: "pubkeyA"}}) - assert.Equal(t, identity.ValidKeysAtTime(200), []Key{{PubKey: "pubkeyB"}}) - assert.Equal(t, identity.ValidKeysAtTime(201), []Key{{PubKey: "pubkeyD"}}) - assert.Equal(t, identity.ValidKeysAtTime(202), []Key{{PubKey: "pubkeyD"}}) - assert.Equal(t, identity.ValidKeysAtTime(300), []Key{{PubKey: "pubkeyE"}}) - assert.Equal(t, identity.ValidKeysAtTime(3000), []Key{{PubKey: "pubkeyE"}}) + assert.Equal(t, identity.ValidKeysAtTime(100), []*Key{{PubKey: "pubkeyA"}}) + assert.Equal(t, identity.ValidKeysAtTime(140), []*Key{{PubKey: "pubkeyA"}}) + assert.Equal(t, identity.ValidKeysAtTime(200), []*Key{{PubKey: "pubkeyB"}}) + assert.Equal(t, identity.ValidKeysAtTime(201), []*Key{{PubKey: "pubkeyD"}}) + assert.Equal(t, identity.ValidKeysAtTime(202), []*Key{{PubKey: "pubkeyD"}}) + assert.Equal(t, identity.ValidKeysAtTime(300), []*Key{{PubKey: "pubkeyE"}}) + assert.Equal(t, identity.ValidKeysAtTime(3000), []*Key{{PubKey: "pubkeyE"}}) } // Test the immutable or mutable metadata search @@ -189,7 +194,7 @@ func TestMetadata(t *testing.T) { assertHasKeyValue(t, identity.MutableMetadata(), "key1", "value1") // try override - identity.AddVersion(&Version{ + identity.addVersionForTest(&Version{ name: "René Descartes", email: "rene.descartes@example.com", }) diff --git a/identity/interface.go b/identity/interface.go index 3407d7ab..d138362d 100644 --- a/identity/interface.go +++ b/identity/interface.go @@ -21,10 +21,10 @@ type Interface interface { AvatarUrl() string // Keys return the last version of the valid keys - Keys() []Key + Keys() []*Key // ValidKeysAtTime return the set of keys valid at a given lamport time - ValidKeysAtTime(time lamport.Time) []Key + ValidKeysAtTime(time lamport.Time) []*Key // DisplayName return a non-empty string to display, representing the // identity, based on the non-empty values. diff --git a/identity/key.go b/identity/key.go index 90edfb60..cc948394 100644 --- a/identity/key.go +++ b/identity/key.go @@ -11,3 +11,8 @@ func (k *Key) Validate() error { return nil } + +func (k *Key) Clone() *Key { + clone := *k + return &clone +} diff --git a/identity/version.go b/identity/version.go index 85195049..f9c7b262 100644 --- a/identity/version.go +++ b/identity/version.go @@ -30,7 +30,7 @@ type Version struct { // The set of keys valid at that time, from this version onward, until they get removed // in a new version. This allow to have multiple key for the same identity (e.g. one per // device) as well as revoke key. - keys []Key + keys []*Key // This optional array is here to ensure a better randomness of the identity id to avoid collisions. // It has no functional purpose and should be ignored. @@ -53,24 +53,22 @@ type VersionJSON struct { Name string `json:"name,omitempty"` Email string `json:"email,omitempty"` AvatarUrl string `json:"avatar_url,omitempty"` - Keys []Key `json:"pub_keys,omitempty"` + Keys []*Key `json:"pub_keys,omitempty"` Nonce []byte `json:"nonce,omitempty"` Metadata map[string]string `json:"metadata,omitempty"` } // Make a deep copy func (v *Version) Clone() *Version { - clone := &Version{ name: v.name, email: v.email, avatarURL: v.avatarURL, - keys: make([]Key, len(v.keys)), - metadata: make(map[string]string), + keys: make([]*Key, len(v.keys)), } - for i, op := range opp.Operations { - clone.Operations[i] = op + for i, key := range v.keys { + clone.keys[i] = key.Clone() } return clone diff --git a/identity/version_test.go b/identity/version_test.go index 8c4c8d99..25848eb5 100644 --- a/identity/version_test.go +++ b/identity/version_test.go @@ -9,11 +9,10 @@ import ( func TestVersionSerialize(t *testing.T) { before := &Version{ - login: "login", name: "name", email: "email", avatarURL: "avatarUrl", - keys: []Key{ + keys: []*Key{ { Fingerprint: "fingerprint1", PubKey: "pubkey1", |