From 45b04351d8d02e53b3401b0ee23f7cbe750b63cd Mon Sep 17 00:00:00 2001 From: Michael Muré Date: Mon, 3 May 2021 11:45:15 +0200 Subject: bug: have a type for combined ids, fix https://github.com/MichaelMure/git-bug/issues/653 --- api/graphql/gen_graphql.go | 33 ----- api/graphql/gqlgen.yml | 5 +- api/graphql/graph/bug.generated.go | 102 ++++++++++++---- api/graphql/graph/identity.generated.go | 33 ++--- api/graphql/graph/mutations.generated.go | 16 +-- api/graphql/graph/operations.generated.go | 193 +++++++++--------------------- api/graphql/graph/prelude.generated.go | 11 ++ api/graphql/graph/root_.generated.go | 97 ++++++++------- api/graphql/graph/timeline.generated.go | 41 +++---- api/graphql/graph/types.generated.go | 17 ++- api/graphql/handler.go | 2 +- api/graphql/models/gen_models.go | 6 +- api/graphql/resolvers/bug.go | 4 - api/graphql/resolvers/comment.go | 5 + api/graphql/resolvers/mutation.go | 10 +- api/graphql/resolvers/operations.go | 24 ---- api/graphql/resolvers/timeline.go | 21 ++-- api/graphql/schema/bug.graphql | 4 +- api/graphql/schema/identity.graphql | 2 +- api/graphql/schema/mutations.graphql | 6 +- api/graphql/schema/operations.graphql | 14 +-- api/graphql/schema/timeline.graphql | 12 +- api/graphql/schema/types.graphql | 1 + api/graphql/tools.go | 8 ++ api/http/git_file_handler.go | 2 +- bridge/github/export_test.go | 7 +- bridge/github/import.go | 3 +- bridge/gitlab/export_test.go | 7 +- bridge/gitlab/import.go | 13 +- bridge/jira/import.go | 5 +- cache/bug_cache.go | 11 +- cache/repo_cache_bug.go | 14 +-- commands/comment.go | 2 +- commands/show.go | 6 +- entities/bug/comment.go | 29 +++-- entities/bug/op_add_comment.go | 15 ++- entities/bug/op_create.go | 13 +- entities/bug/op_create_test.go | 56 +++------ entities/bug/op_edit_comment.go | 17 +-- entities/bug/op_label_change.go | 31 ++--- entities/bug/op_set_status.go | 25 ++-- entities/bug/op_set_title.go | 29 +++-- entities/bug/snapshot.go | 23 +++- entities/bug/timeline.go | 42 +++---- entity/id.go | 18 --- entity/id_interleaved.go | 84 ++++++++++++- entity/id_interleaved_test.go | 2 +- go.mod | 2 + go.sum | 2 + repository/gogit.go | 1 + termui/show_bug.go | 8 +- termui/termui.go | 2 +- 52 files changed, 583 insertions(+), 553 deletions(-) delete mode 100644 api/graphql/gen_graphql.go create mode 100644 api/graphql/tools.go diff --git a/api/graphql/gen_graphql.go b/api/graphql/gen_graphql.go deleted file mode 100644 index 19c5f1e3..00000000 --- a/api/graphql/gen_graphql.go +++ /dev/null @@ -1,33 +0,0 @@ -//go:build ignore - -package main - -import ( - "fmt" - "io/ioutil" - "log" - "os" - - "github.com/99designs/gqlgen/api" - "github.com/99designs/gqlgen/codegen/config" - "github.com/pkg/errors" -) - -func main() { - fmt.Println("Generating graphql code ...") - - log.SetOutput(ioutil.Discard) - - cfg, err := config.LoadConfigFromDefaultLocations() - if os.IsNotExist(errors.Cause(err)) { - cfg = config.DefaultConfig() - } else if err != nil { - _, _ = fmt.Fprintln(os.Stderr, err.Error()) - os.Exit(2) - } - - if err = api.Generate(cfg); err != nil { - _, _ = fmt.Fprintln(os.Stderr, err.Error()) - os.Exit(3) - } -} diff --git a/api/graphql/gqlgen.yml b/api/graphql/gqlgen.yml index fbb46db6..98d31a5c 100644 --- a/api/graphql/gqlgen.yml +++ b/api/graphql/gqlgen.yml @@ -6,17 +6,18 @@ exec: model: filename: models/gen_models.go -skip_mod_tidy: true - autobind: - "github.com/MichaelMure/git-bug/api/graphql/models" - "github.com/MichaelMure/git-bug/repository" + - "github.com/MichaelMure/git-bug/entity" - "github.com/MichaelMure/git-bug/entity/dag" - "github.com/MichaelMure/git-bug/entities/common" - "github.com/MichaelMure/git-bug/entities/bug" - "github.com/MichaelMure/git-bug/entities/identity" models: + ID: + model: github.com/MichaelMure/git-bug/entity.Id Bug: model: github.com/MichaelMure/git-bug/api/graphql/models.BugWrapper Color: diff --git a/api/graphql/graph/bug.generated.go b/api/graphql/graph/bug.generated.go index 67af1933..ab9c6c34 100644 --- a/api/graphql/graph/bug.generated.go +++ b/api/graphql/graph/bug.generated.go @@ -15,6 +15,7 @@ import ( "github.com/MichaelMure/git-bug/api/graphql/models" "github.com/MichaelMure/git-bug/entities/bug" "github.com/MichaelMure/git-bug/entities/common" + "github.com/MichaelMure/git-bug/entity" "github.com/MichaelMure/git-bug/repository" "github.com/vektah/gqlparser/v2/ast" ) @@ -22,7 +23,6 @@ import ( // region ************************** generated!.gotpl ************************** type BugResolver interface { - ID(ctx context.Context, obj models.BugWrapper) (string, error) HumanID(ctx context.Context, obj models.BugWrapper) (string, error) Actors(ctx context.Context, obj models.BugWrapper, after *string, before *string, first *int, last *int) (*models.IdentityConnection, error) @@ -32,6 +32,7 @@ type BugResolver interface { Operations(ctx context.Context, obj models.BugWrapper, after *string, before *string, first *int, last *int) (*models.OperationConnection, error) } type CommentResolver interface { + ID(ctx context.Context, obj *bug.Comment) (entity.CombinedId, error) Author(ctx context.Context, obj *bug.Comment) (models.IdentityWrapper, error) } @@ -271,7 +272,7 @@ func (ec *executionContext) _Bug_id(ctx context.Context, field graphql.Collected }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Bug().ID(rctx, obj) + return obj.Id(), nil }) if err != nil { ec.Error(ctx, err) @@ -283,9 +284,9 @@ func (ec *executionContext) _Bug_id(ctx context.Context, field graphql.Collected } return graphql.Null } - res := resTmp.(string) + res := resTmp.(entity.Id) fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) + return ec.marshalNID2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋentityᚐId(ctx, field.Selections, res) } func (ec *executionContext) fieldContext_Bug_id(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { @@ -293,9 +294,9 @@ func (ec *executionContext) fieldContext_Bug_id(ctx context.Context, field graph Object: "Bug", Field: field, IsMethod: true, - IsResolver: true, + IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") + return nil, errors.New("field of type ID does not have child fields") }, } return fc, nil @@ -1294,6 +1295,50 @@ func (ec *executionContext) fieldContext_BugEdge_node(ctx context.Context, field return fc, nil } +func (ec *executionContext) _Comment_id(ctx context.Context, field graphql.CollectedField, obj *bug.Comment) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Comment_id(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Comment().ID(rctx, obj) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(entity.CombinedId) + fc.Result = res + return ec.marshalNCombinedId2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋentityᚐCombinedId(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Comment_id(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Comment", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type CombinedId does not have child fields") + }, + } + return fc, nil +} + func (ec *executionContext) _Comment_author(ctx context.Context, field graphql.CollectedField, obj *bug.Comment) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Comment_author(ctx, field) if err != nil { @@ -1533,6 +1578,8 @@ func (ec *executionContext) fieldContext_CommentConnection_nodes(ctx context.Con IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { switch field.Name { + case "id": + return ec.fieldContext_Comment_id(ctx, field) case "author": return ec.fieldContext_Comment_author(ctx, field) case "message": @@ -1727,6 +1774,8 @@ func (ec *executionContext) fieldContext_CommentEdge_node(ctx context.Context, f IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { switch field.Name { + case "id": + return ec.fieldContext_Comment_id(ctx, field) case "author": return ec.fieldContext_Comment_author(ctx, field) case "message": @@ -1763,25 +1812,12 @@ func (ec *executionContext) _Bug(ctx context.Context, sel ast.SelectionSet, obj case "__typename": out.Values[i] = graphql.MarshalString("Bug") case "id": - field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - } - }() - res = ec._Bug_id(ctx, field, obj) - if res == graphql.Null { - atomic.AddUint32(&invalids, 1) - } - return res - } - - out.Concurrently(i, func() graphql.Marshaler { - return innerFunc(ctx) + out.Values[i] = ec._Bug_id(ctx, field, obj) - }) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&invalids, 1) + } case "humanId": field := field @@ -2049,6 +2085,26 @@ func (ec *executionContext) _Comment(ctx context.Context, sel ast.SelectionSet, switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("Comment") + case "id": + field := field + + innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Comment_id(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&invalids, 1) + } + return res + } + + out.Concurrently(i, func() graphql.Marshaler { + return innerFunc(ctx) + + }) case "author": field := field diff --git a/api/graphql/graph/identity.generated.go b/api/graphql/graph/identity.generated.go index 7fa9b9f0..51aa188b 100644 --- a/api/graphql/graph/identity.generated.go +++ b/api/graphql/graph/identity.generated.go @@ -12,13 +12,13 @@ import ( "github.com/99designs/gqlgen/graphql" "github.com/MichaelMure/git-bug/api/graphql/models" + "github.com/MichaelMure/git-bug/entity" "github.com/vektah/gqlparser/v2/ast" ) // region ************************** generated!.gotpl ************************** type IdentityResolver interface { - ID(ctx context.Context, obj models.IdentityWrapper) (string, error) HumanID(ctx context.Context, obj models.IdentityWrapper) (string, error) } @@ -48,7 +48,7 @@ func (ec *executionContext) _Identity_id(ctx context.Context, field graphql.Coll }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Identity().ID(rctx, obj) + return obj.Id(), nil }) if err != nil { ec.Error(ctx, err) @@ -60,9 +60,9 @@ func (ec *executionContext) _Identity_id(ctx context.Context, field graphql.Coll } return graphql.Null } - res := resTmp.(string) + res := resTmp.(entity.Id) fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) + return ec.marshalNID2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋentityᚐId(ctx, field.Selections, res) } func (ec *executionContext) fieldContext_Identity_id(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { @@ -70,9 +70,9 @@ func (ec *executionContext) fieldContext_Identity_id(ctx context.Context, field Object: "Identity", Field: field, IsMethod: true, - IsResolver: true, + IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") + return nil, errors.New("field of type ID does not have child fields") }, } return fc, nil @@ -713,25 +713,12 @@ func (ec *executionContext) _Identity(ctx context.Context, sel ast.SelectionSet, case "__typename": out.Values[i] = graphql.MarshalString("Identity") case "id": - field := field - - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - } - }() - res = ec._Identity_id(ctx, field, obj) - if res == graphql.Null { - atomic.AddUint32(&invalids, 1) - } - return res - } - out.Concurrently(i, func() graphql.Marshaler { - return innerFunc(ctx) + out.Values[i] = ec._Identity_id(ctx, field, obj) - }) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&invalids, 1) + } case "humanId": field := field diff --git a/api/graphql/graph/mutations.generated.go b/api/graphql/graph/mutations.generated.go index 9d5df56e..11604d11 100644 --- a/api/graphql/graph/mutations.generated.go +++ b/api/graphql/graph/mutations.generated.go @@ -2097,7 +2097,7 @@ func (ec *executionContext) unmarshalInputEditCommentInput(ctx context.Context, asMap[k] = v } - fieldsInOrder := [...]string{"clientMutationId", "repoRef", "prefix", "target", "message", "files"} + fieldsInOrder := [...]string{"clientMutationId", "repoRef", "targetPrefix", "message", "files"} for _, k := range fieldsInOrder { v, ok := asMap[k] if !ok { @@ -2120,19 +2120,11 @@ func (ec *executionContext) unmarshalInputEditCommentInput(ctx context.Context, if err != nil { return it, err } - case "prefix": - var err error - - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("prefix")) - it.Prefix, err = ec.unmarshalNString2string(ctx, v) - if err != nil { - return it, err - } - case "target": + case "targetPrefix": var err error - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("target")) - it.Target, err = ec.unmarshalNString2string(ctx, v) + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("targetPrefix")) + it.TargetPrefix, err = ec.unmarshalNString2string(ctx, v) if err != nil { return it, err } diff --git a/api/graphql/graph/operations.generated.go b/api/graphql/graph/operations.generated.go index 39a216f4..cc3cd9b7 100644 --- a/api/graphql/graph/operations.generated.go +++ b/api/graphql/graph/operations.generated.go @@ -15,6 +15,7 @@ import ( "github.com/MichaelMure/git-bug/api/graphql/models" "github.com/MichaelMure/git-bug/entities/bug" "github.com/MichaelMure/git-bug/entities/common" + "github.com/MichaelMure/git-bug/entity" "github.com/MichaelMure/git-bug/entity/dag" "github.com/MichaelMure/git-bug/repository" "github.com/vektah/gqlparser/v2/ast" @@ -23,33 +24,27 @@ import ( // region ************************** generated!.gotpl ************************** type AddCommentOperationResolver interface { - ID(ctx context.Context, obj *bug.AddCommentOperation) (string, error) Author(ctx context.Context, obj *bug.AddCommentOperation) (models.IdentityWrapper, error) Date(ctx context.Context, obj *bug.AddCommentOperation) (*time.Time, error) } type CreateOperationResolver interface { - ID(ctx context.Context, obj *bug.CreateOperation) (string, error) Author(ctx context.Context, obj *bug.CreateOperation) (models.IdentityWrapper, error) Date(ctx context.Context, obj *bug.CreateOperation) (*time.Time, error) } type EditCommentOperationResolver interface { - ID(ctx context.Context, obj *bug.EditCommentOperation) (string, error) Author(ctx context.Context, obj *bug.EditCommentOperation) (models.IdentityWrapper, error) Date(ctx context.Context, obj *bug.EditCommentOperation) (*time.Time, error) Target(ctx context.Context, obj *bug.EditCommentOperation) (string, error) } type LabelChangeOperationResolver interface { - ID(ctx context.Context, obj *bug.LabelChangeOperation) (string, error) Author(ctx context.Context, obj *bug.LabelChangeOperation) (models.IdentityWrapper, error) Date(ctx context.Context, obj *bug.LabelChangeOperation) (*time.Time, error) } type SetStatusOperationResolver interface { - ID(ctx context.Context, obj *bug.SetStatusOperation) (string, error) Author(ctx context.Context, obj *bug.SetStatusOperation) (models.IdentityWrapper, error) Date(ctx context.Context, obj *bug.SetStatusOperation) (*time.Time, error) } type SetTitleOperationResolver interface { - ID(ctx context.Context, obj *bug.SetTitleOperation) (string, error) Author(ctx context.Context, obj *bug.SetTitleOperation) (models.IdentityWrapper, error) Date(ctx context.Context, obj *bug.SetTitleOperation) (*time.Time, error) } @@ -80,7 +75,7 @@ func (ec *executionContext) _AddCommentOperation_id(ctx context.Context, field g }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.AddCommentOperation().ID(rctx, obj) + return obj.Id(), nil }) if err != nil { ec.Error(ctx, err) @@ -92,9 +87,9 @@ func (ec *executionContext) _AddCommentOperation_id(ctx context.Context, field g } return graphql.Null } - res := resTmp.(string) + res := resTmp.(entity.Id) fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) + return ec.marshalNID2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋentityᚐId(ctx, field.Selections, res) } func (ec *executionContext) fieldContext_AddCommentOperation_id(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { @@ -102,9 +97,9 @@ func (ec *executionContext) fieldContext_AddCommentOperation_id(ctx context.Cont Object: "AddCommentOperation", Field: field, IsMethod: true, - IsResolver: true, + IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") + return nil, errors.New("field of type ID does not have child fields") }, } return fc, nil @@ -318,7 +313,7 @@ func (ec *executionContext) _CreateOperation_id(ctx context.Context, field graph }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.CreateOperation().ID(rctx, obj) + return obj.Id(), nil }) if err != nil { ec.Error(ctx, err) @@ -330,9 +325,9 @@ func (ec *executionContext) _CreateOperation_id(ctx context.Context, field graph } return graphql.Null } - res := resTmp.(string) + res := resTmp.(entity.Id) fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) + return ec.marshalNID2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋentityᚐId(ctx, field.Selections, res) } func (ec *executionContext) fieldContext_CreateOperation_id(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { @@ -340,9 +335,9 @@ func (ec *executionContext) fieldContext_CreateOperation_id(ctx context.Context, Object: "CreateOperation", Field: field, IsMethod: true, - IsResolver: true, + IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") + return nil, errors.New("field of type ID does not have child fields") }, } return fc, nil @@ -600,7 +595,7 @@ func (ec *executionContext) _EditCommentOperation_id(ctx context.Context, field }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.EditCommentOperation().ID(rctx, obj) + return obj.Id(), nil }) if err != nil { ec.Error(ctx, err) @@ -612,9 +607,9 @@ func (ec *executionContext) _EditCommentOperation_id(ctx context.Context, field } return graphql.Null } - res := resTmp.(string) + res := resTmp.(entity.Id) fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) + return ec.marshalNID2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋentityᚐId(ctx, field.Selections, res) } func (ec *executionContext) fieldContext_EditCommentOperation_id(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { @@ -622,9 +617,9 @@ func (ec *executionContext) fieldContext_EditCommentOperation_id(ctx context.Con Object: "EditCommentOperation", Field: field, IsMethod: true, - IsResolver: true, + IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") + return nil, errors.New("field of type ID does not have child fields") }, } return fc, nil @@ -882,7 +877,7 @@ func (ec *executionContext) _LabelChangeOperation_id(ctx context.Context, field }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.LabelChangeOperation().ID(rctx, obj) + return obj.Id(), nil }) if err != nil { ec.Error(ctx, err) @@ -894,9 +889,9 @@ func (ec *executionContext) _LabelChangeOperation_id(ctx context.Context, field } return graphql.Null } - res := resTmp.(string) + res := resTmp.(entity.Id) fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) + return ec.marshalNID2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋentityᚐId(ctx, field.Selections, res) } func (ec *executionContext) fieldContext_LabelChangeOperation_id(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { @@ -904,9 +899,9 @@ func (ec *executionContext) fieldContext_LabelChangeOperation_id(ctx context.Con Object: "LabelChangeOperation", Field: field, IsMethod: true, - IsResolver: true, + IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") + return nil, errors.New("field of type ID does not have child fields") }, } return fc, nil @@ -1412,7 +1407,7 @@ func (ec *executionContext) _SetStatusOperation_id(ctx context.Context, field gr }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.SetStatusOperation().ID(rctx, obj) + return obj.Id(), nil }) if err != nil { ec.Error(ctx, err) @@ -1424,9 +1419,9 @@ func (ec *executionContext) _SetStatusOperation_id(ctx context.Context, field gr } return graphql.Null } - res := resTmp.(string) + res := resTmp.(entity.Id) fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) + return ec.marshalNID2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋentityᚐId(ctx, field.Selections, res) } func (ec *executionContext) fieldContext_SetStatusOperation_id(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { @@ -1434,9 +1429,9 @@ func (ec *executionContext) fieldContext_SetStatusOperation_id(ctx context.Conte Object: "SetStatusOperation", Field: field, IsMethod: true, - IsResolver: true, + IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") + return nil, errors.New("field of type ID does not have child fields") }, } return fc, nil @@ -1606,7 +1601,7 @@ func (ec *executionContext) _SetTitleOperation_id(ctx context.Context, field gra }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.SetTitleOperation().ID(rctx, obj) + return obj.Id(), nil }) if err != nil { ec.Error(ctx, err) @@ -1618,9 +1613,9 @@ func (ec *executionContext) _SetTitleOperation_id(ctx context.Context, field gra } return graphql.Null } - res := resTmp.(string) + res := resTmp.(entity.Id) fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) + return ec.marshalNID2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋentityᚐId(ctx, field.Selections, res) } func (ec *executionContext) fieldContext_SetTitleOperation_id(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { @@ -1628,9 +1623,9 @@ func (ec *executionContext) fieldContext_SetTitleOperation_id(ctx context.Contex Object: "SetTitleOperation", Field: field, IsMethod: true, - IsResolver: true, + IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") + return nil, errors.New("field of type ID does not have child fields") }, } return fc, nil @@ -1892,25 +1887,12 @@ func (ec *executionContext) _AddCommentOperation(ctx context.Context, sel ast.Se case "__typename": out.Values[i] = graphql.MarshalString("AddCommentOperation") case "id": - field := field - - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - } - }() - res = ec._AddCommentOperation_id(ctx, field, obj) - if res == graphql.Null { - atomic.AddUint32(&invalids, 1) - } - return res - } - out.Concurrently(i, func() graphql.Marshaler { - return innerFunc(ctx) + out.Values[i] = ec._AddCommentOperation_id(ctx, field, obj) - }) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&invalids, 1) + } case "author": field := field @@ -1987,25 +1969,12 @@ func (ec *executionContext) _CreateOperation(ctx context.Context, sel ast.Select case "__typename": out.Values[i] = graphql.MarshalString("CreateOperation") case "id": - field := field - - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - } - }() - res = ec._CreateOperation_id(ctx, field, obj) - if res == graphql.Null { - atomic.AddUint32(&invalids, 1) - } - return res - } - out.Concurrently(i, func() graphql.Marshaler { - return innerFunc(ctx) + out.Values[i] = ec._CreateOperation_id(ctx, field, obj) - }) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&invalids, 1) + } case "author": field := field @@ -2089,25 +2058,12 @@ func (ec *executionContext) _EditCommentOperation(ctx context.Context, sel ast.S case "__typename": out.Values[i] = graphql.MarshalString("EditCommentOperation") case "id": - field := field - - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - } - }() - res = ec._EditCommentOperation_id(ctx, field, obj) - if res == graphql.Null { - atomic.AddUint32(&invalids, 1) - } - return res - } - out.Concurrently(i, func() graphql.Marshaler { - return innerFunc(ctx) + out.Values[i] = ec._EditCommentOperation_id(ctx, field, obj) - }) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&invalids, 1) + } case "author": field := field @@ -2204,25 +2160,12 @@ func (ec *executionContext) _LabelChangeOperation(ctx context.Context, sel ast.S case "__typename": out.Values[i] = graphql.MarshalString("LabelChangeOperation") case "id": - field := field - - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - } - }() - res = ec._LabelChangeOperation_id(ctx, field, obj) - if res == graphql.Null { - atomic.AddUint32(&invalids, 1) - } - return res - } - out.Concurrently(i, func() graphql.Marshaler { - return innerFunc(ctx) + out.Values[i] = ec._LabelChangeOperation_id(ctx, field, obj) - }) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&invalids, 1) + } case "author": field := field @@ -2383,25 +2326,12 @@ func (ec *executionContext) _SetStatusOperation(ctx context.Context, sel ast.Sel case "__typename": out.Values[i] = graphql.MarshalString("SetStatusOperation") case "id": - field := field - - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - } - }() - res = ec._SetStatusOperation_id(ctx, field, obj) - if res == graphql.Null { - atomic.AddUint32(&invalids, 1) - } - return res - } - out.Concurrently(i, func() graphql.Marshaler { - return innerFunc(ctx) + out.Values[i] = ec._SetStatusOperation_id(ctx, field, obj) - }) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&invalids, 1) + } case "author": field := field @@ -2471,25 +2401,12 @@ func (ec *executionContext) _SetTitleOperation(ctx context.Context, sel ast.Sele case "__typename": out.Values[i] = graphql.MarshalString("SetTitleOperation") case "id": - field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - } - }() - res = ec._SetTitleOperation_id(ctx, field, obj) - if res == graphql.Null { - atomic.AddUint32(&invalids, 1) - } - return res - } - - out.Concurrently(i, func() graphql.Marshaler { - return innerFunc(ctx) + out.Values[i] = ec._SetTitleOperation_id(ctx, field, obj) - }) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&invalids, 1) + } case "author": field := field diff --git a/api/graphql/graph/prelude.generated.go b/api/graphql/graph/prelude.generated.go index 81978efa..3767b0cd 100644 --- a/api/graphql/graph/prelude.generated.go +++ b/api/graphql/graph/prelude.generated.go @@ -11,6 +11,7 @@ import ( "github.com/99designs/gqlgen/graphql" "github.com/99designs/gqlgen/graphql/introspection" + "github.com/MichaelMure/git-bug/entity" "github.com/vektah/gqlparser/v2/ast" ) @@ -2172,6 +2173,16 @@ func (ec *executionContext) marshalNBoolean2bool(ctx context.Context, sel ast.Se return res } +func (ec *executionContext) unmarshalNID2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋentityᚐId(ctx context.Context, v interface{}) (entity.Id, error) { + var res entity.Id + err := res.UnmarshalGQL(v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalNID2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋentityᚐId(ctx context.Context, sel ast.SelectionSet, v entity.Id) graphql.Marshaler { + return v +} + func (ec *executionContext) unmarshalNInt2int(ctx context.Context, v interface{}) (int, error) { res, err := graphql.UnmarshalInt(v) return res, graphql.ErrorOnPath(ctx, err) diff --git a/api/graphql/graph/root_.generated.go b/api/graphql/graph/root_.generated.go index 297fc6a2..a1434228 100644 --- a/api/graphql/graph/root_.generated.go +++ b/api/graphql/graph/root_.generated.go @@ -74,7 +74,7 @@ type ComplexityRoot struct { Author func(childComplexity int) int Date func(childComplexity int) int Files func(childComplexity int) int - ID func(childComplexity int) int + Id func(childComplexity int) int Message func(childComplexity int) int } @@ -102,7 +102,7 @@ type ComplexityRoot struct { Comments func(childComplexity int, after *string, before *string, first *int, last *int) int CreatedAt func(childComplexity int) int HumanID func(childComplexity int) int - ID func(childComplexity int) int + Id func(childComplexity int) int Labels func(childComplexity int) int LastEdit func(childComplexity int) int Operations func(childComplexity int, after *string, before *string, first *int, last *int) int @@ -146,6 +146,7 @@ type ComplexityRoot struct { Comment struct { Author func(childComplexity int) int Files func(childComplexity int) int + ID func(childComplexity int) int Message func(childComplexity int) int } @@ -170,7 +171,7 @@ type ComplexityRoot struct { Author func(childComplexity int) int Date func(childComplexity int) int Files func(childComplexity int) int - ID func(childComplexity int) int + Id func(childComplexity int) int Message func(childComplexity int) int Title func(childComplexity int) int } @@ -191,7 +192,7 @@ type ComplexityRoot struct { Author func(childComplexity int) int Date func(childComplexity int) int Files func(childComplexity int) int - ID func(childComplexity int) int + Id func(childComplexity int) int Message func(childComplexity int) int Target func(childComplexity int) int } @@ -207,7 +208,7 @@ type ComplexityRoot struct { DisplayName func(childComplexity int) int Email func(childComplexity int) int HumanID func(childComplexity int) int - ID func(childComplexity int) int + Id func(childComplexity int) int IsProtected func(childComplexity int) int Login func(childComplexity int) int Name func(childComplexity int) int @@ -234,7 +235,7 @@ type ComplexityRoot struct { Added func(childComplexity int) int Author func(childComplexity int) int Date func(childComplexity int) int - ID func(childComplexity int) int + Id func(childComplexity int) int Removed func(childComplexity int) int } @@ -323,7 +324,7 @@ type ComplexityRoot struct { SetStatusOperation struct { Author func(childComplexity int) int Date func(childComplexity int) int - ID func(childComplexity int) int + Id func(childComplexity int) int Status func(childComplexity int) int } @@ -337,7 +338,7 @@ type ComplexityRoot struct { SetTitleOperation struct { Author func(childComplexity int) int Date func(childComplexity int) int - ID func(childComplexity int) int + Id func(childComplexity int) int Title func(childComplexity int) int Was func(childComplexity int) int } @@ -462,11 +463,11 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.AddCommentOperation.Files(childComplexity), true case "AddCommentOperation.id": - if e.complexity.AddCommentOperation.ID == nil { + if e.complexity.AddCommentOperation.Id == nil { break } - return e.complexity.AddCommentOperation.ID(childComplexity), true + return e.complexity.AddCommentOperation.Id(childComplexity), true case "AddCommentOperation.message": if e.complexity.AddCommentOperation.Message == nil { @@ -605,11 +606,11 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Bug.HumanID(childComplexity), true case "Bug.id": - if e.complexity.Bug.ID == nil { + if e.complexity.Bug.Id == nil { break } - return e.complexity.Bug.ID(childComplexity), true + return e.complexity.Bug.Id(childComplexity), true case "Bug.labels": if e.complexity.Bug.Labels == nil { @@ -801,6 +802,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Comment.Files(childComplexity), true + case "Comment.id": + if e.complexity.Comment.ID == nil { + break + } + + return e.complexity.Comment.ID(childComplexity), true + case "Comment.message": if e.complexity.Comment.Message == nil { break @@ -886,11 +894,11 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.CreateOperation.Files(childComplexity), true case "CreateOperation.id": - if e.complexity.CreateOperation.ID == nil { + if e.complexity.CreateOperation.Id == nil { break } - return e.complexity.CreateOperation.ID(childComplexity), true + return e.complexity.CreateOperation.Id(childComplexity), true case "CreateOperation.message": if e.complexity.CreateOperation.Message == nil { @@ -991,11 +999,11 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.EditCommentOperation.Files(childComplexity), true case "EditCommentOperation.id": - if e.complexity.EditCommentOperation.ID == nil { + if e.complexity.EditCommentOperation.Id == nil { break } - return e.complexity.EditCommentOperation.ID(childComplexity), true + return e.complexity.EditCommentOperation.Id(childComplexity), true case "EditCommentOperation.message": if e.complexity.EditCommentOperation.Message == nil { @@ -1061,11 +1069,11 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Identity.HumanID(childComplexity), true case "Identity.id": - if e.complexity.Identity.ID == nil { + if e.complexity.Identity.Id == nil { break } - return e.complexity.Identity.ID(childComplexity), true + return e.complexity.Identity.Id(childComplexity), true case "Identity.isProtected": if e.complexity.Identity.IsProtected == nil { @@ -1166,11 +1174,11 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.LabelChangeOperation.Date(childComplexity), true case "LabelChangeOperation.id": - if e.complexity.LabelChangeOperation.ID == nil { + if e.complexity.LabelChangeOperation.Id == nil { break } - return e.complexity.LabelChangeOperation.ID(childComplexity), true + return e.complexity.LabelChangeOperation.Id(childComplexity), true case "LabelChangeOperation.removed": if e.complexity.LabelChangeOperation.Removed == nil { @@ -1591,11 +1599,11 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.SetStatusOperation.Date(childComplexity), true case "SetStatusOperation.id": - if e.complexity.SetStatusOperation.ID == nil { + if e.complexity.SetStatusOperation.Id == nil { break } - return e.complexity.SetStatusOperation.ID(childComplexity), true + return e.complexity.SetStatusOperation.Id(childComplexity), true case "SetStatusOperation.status": if e.complexity.SetStatusOperation.Status == nil { @@ -1647,11 +1655,11 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.SetTitleOperation.Date(childComplexity), true case "SetTitleOperation.id": - if e.complexity.SetTitleOperation.ID == nil { + if e.complexity.SetTitleOperation.Id == nil { break } - return e.complexity.SetTitleOperation.ID(childComplexity), true + return e.complexity.SetTitleOperation.Id(childComplexity), true case "SetTitleOperation.title": if e.complexity.SetTitleOperation.Title == nil { @@ -1844,6 +1852,8 @@ func (ec *executionContext) introspectType(name string) (*introspection.Type, er var sources = []*ast.Source{ {Name: "../schema/bug.graphql", Input: `"""Represents a comment on a bug.""" type Comment implements Authored { + id: CombinedId! + """The author of this comment.""" author: Identity! @@ -1873,7 +1883,7 @@ enum Status { type Bug implements Authored { """The identifier for this bug""" - id: String! + id: ID! """The human version (truncated) identifier for this bug""" humanId: String! status: Status! @@ -1964,7 +1974,7 @@ type BugEdge { {Name: "../schema/identity.graphql", Input: `"""Represents an identity""" type Identity { """The identifier for this identity""" - id: String! + id: ID! """The human version (truncated) identifier for this identity""" humanId: String! """The name of the person, if known.""" @@ -2109,10 +2119,8 @@ input EditCommentInput { 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 ID of the comment to be changed.""" - target: String! + """A prefix of the CombinedId of the comment to be changed.""" + targetPrefix: String! """The new message to be set.""" message: String! """The collection of file's hash required for the first message.""" @@ -2226,7 +2234,7 @@ type SetTitlePayload { {Name: "../schema/operations.graphql", Input: `"""An operation applied to a bug.""" interface Operation { """The identifier of the operation""" - id: String! + id: ID! """The operations author.""" author: Identity! """The datetime when this operation was issued.""" @@ -2253,7 +2261,7 @@ type OperationEdge { type CreateOperation implements Operation & Authored { """The identifier of the operation""" - id: String! + id: ID! """The author of this object.""" author: Identity! """The datetime when this operation was issued.""" @@ -2266,7 +2274,7 @@ type CreateOperation implements Operation & Authored { type SetTitleOperation implements Operation & Authored { """The identifier of the operation""" - id: String! + id: ID! """The author of this object.""" author: Identity! """The datetime when this operation was issued.""" @@ -2278,7 +2286,7 @@ type SetTitleOperation implements Operation & Authored { type AddCommentOperation implements Operation & Authored { """The identifier of the operation""" - id: String! + id: ID! """The author of this object.""" author: Identity! """The datetime when this operation was issued.""" @@ -2290,7 +2298,7 @@ type AddCommentOperation implements Operation & Authored { type EditCommentOperation implements Operation & Authored { """The identifier of the operation""" - id: String! + id: ID! """The author of this object.""" author: Identity! """The datetime when this operation was issued.""" @@ -2303,7 +2311,7 @@ type EditCommentOperation implements Operation & Authored { type SetStatusOperation implements Operation & Authored { """The identifier of the operation""" - id: String! + id: ID! """The author of this object.""" author: Identity! """The datetime when this operation was issued.""" @@ -2314,7 +2322,7 @@ type SetStatusOperation implements Operation & Authored { type LabelChangeOperation implements Operation & Authored { """The identifier of the operation""" - id: String! + id: ID! """The author of this object.""" author: Identity! """The datetime when this operation was issued.""" @@ -2404,7 +2412,7 @@ type Mutation { {Name: "../schema/timeline.graphql", Input: `"""An item in the timeline of events""" interface TimelineItem { """The identifier of the source operation""" - id: String! + id: CombinedId! } """CommentHistoryStep hold one version of a message in the history""" @@ -2434,7 +2442,7 @@ type TimelineItemEdge { """CreateTimelineItem is a TimelineItem that represent the creation of a bug and its message edition history""" type CreateTimelineItem implements TimelineItem & Authored { """The identifier of the source operation""" - id: String! + id: CombinedId! author: Identity! message: String! messageIsEmpty: Boolean! @@ -2448,7 +2456,7 @@ type CreateTimelineItem implements TimelineItem & Authored { """AddCommentTimelineItem is a TimelineItem that represent a Comment and its edition history""" type AddCommentTimelineItem implements TimelineItem & Authored { """The identifier of the source operation""" - id: String! + id: CombinedId! author: Identity! message: String! messageIsEmpty: Boolean! @@ -2462,7 +2470,7 @@ type AddCommentTimelineItem implements TimelineItem & Authored { """LabelChangeTimelineItem is a TimelineItem that represent a change in the labels of a bug""" type LabelChangeTimelineItem implements TimelineItem & Authored { """The identifier of the source operation""" - id: String! + id: CombinedId! author: Identity! date: Time! added: [Label!]! @@ -2472,7 +2480,7 @@ type LabelChangeTimelineItem implements TimelineItem & Authored { """SetStatusTimelineItem is a TimelineItem that represent a change in the status of a bug""" type SetStatusTimelineItem implements TimelineItem & Authored { """The identifier of the source operation""" - id: String! + id: CombinedId! author: Identity! date: Time! status: Status! @@ -2481,14 +2489,15 @@ type SetStatusTimelineItem implements TimelineItem & Authored { """LabelChangeTimelineItem is a TimelineItem that represent a change in the title of a bug""" type SetTitleTimelineItem implements TimelineItem & Authored { """The identifier of the source operation""" - id: String! + id: CombinedId! author: Identity! date: Time! title: String! was: String! } `, BuiltIn: false}, - {Name: "../schema/types.graphql", Input: `scalar Time + {Name: "../schema/types.graphql", Input: `scalar CombinedId +scalar Time scalar Hash """Defines a color by red, green and blue components.""" diff --git a/api/graphql/graph/timeline.generated.go b/api/graphql/graph/timeline.generated.go index 9ad32e0b..4833e274 100644 --- a/api/graphql/graph/timeline.generated.go +++ b/api/graphql/graph/timeline.generated.go @@ -15,6 +15,7 @@ import ( "github.com/MichaelMure/git-bug/api/graphql/models" "github.com/MichaelMure/git-bug/entities/bug" "github.com/MichaelMure/git-bug/entities/common" + "github.com/MichaelMure/git-bug/entity" "github.com/MichaelMure/git-bug/repository" "github.com/vektah/gqlparser/v2/ast" ) @@ -22,7 +23,7 @@ import ( // region ************************** generated!.gotpl ************************** type AddCommentTimelineItemResolver interface { - ID(ctx context.Context, obj *bug.AddCommentTimelineItem) (string, error) + ID(ctx context.Context, obj *bug.AddCommentTimelineItem) (entity.CombinedId, error) Author(ctx context.Context, obj *bug.AddCommentTimelineItem) (models.IdentityWrapper, error) CreatedAt(ctx context.Context, obj *bug.AddCommentTimelineItem) (*time.Time, error) @@ -32,24 +33,24 @@ type CommentHistoryStepResolver interface { Date(ctx context.Context, obj *bug.CommentHistoryStep) (*time.Time, error) } type CreateTimelineItemResolver interface { - ID(ctx context.Context, obj *bug.CreateTimelineItem) (string, error) + ID(ctx context.Context, obj *bug.CreateTimelineItem) (entity.CombinedId, error) Author(ctx context.Context, obj *bug.CreateTimelineItem) (models.IdentityWrapper, error) CreatedAt(ctx context.Context, obj *bug.CreateTimelineItem) (*time.Time, error) LastEdit(ctx context.Context, obj *bug.CreateTimelineItem) (*time.Time, error) } type LabelChangeTimelineItemResolver interface { - ID(ctx context.Context, obj *bug.LabelChangeTimelineItem) (string, error) + ID(ctx context.Context, obj *bug.LabelChangeTimelineItem) (entity.CombinedId, error) Author(ctx context.Context, obj *bug.LabelChangeTimelineItem) (models.IdentityWrapper, error) Date(ctx context.Context, obj *bug.LabelChangeTimelineItem) (*time.Time, error) } type SetStatusTimelineItemResolver interface { - ID(ctx context.Context, obj *bug.SetStatusTimelineItem) (string, error) + ID(ctx context.Context, obj *bug.SetStatusTimelineItem) (entity.CombinedId, error) Author(ctx context.Context, obj *bug.SetStatusTimelineItem) (models.IdentityWrapper, error) Date(ctx context.Context, obj *bug.SetStatusTimelineItem) (*time.Time, error) } type SetTitleTimelineItemResolver interface { - ID(ctx context.Context, obj *bug.SetTitleTimelineItem) (string, error) + ID(ctx context.Context, obj *bug.SetTitleTimelineItem) (entity.CombinedId, error) Author(ctx context.Context, obj *bug.SetTitleTimelineItem) (models.IdentityWrapper, error) Date(ctx context.Context, obj *bug.SetTitleTimelineItem) (*time.Time, error) } @@ -92,9 +93,9 @@ func (ec *executionContext) _AddCommentTimelineItem_id(ctx context.Context, fiel } return graphql.Null } - res := resTmp.(string) + res := resTmp.(entity.CombinedId) fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) + return ec.marshalNCombinedId2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋentityᚐCombinedId(ctx, field.Selections, res) } func (ec *executionContext) fieldContext_AddCommentTimelineItem_id(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { @@ -104,7 +105,7 @@ func (ec *executionContext) fieldContext_AddCommentTimelineItem_id(ctx context.C IsMethod: true, IsResolver: true, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") + return nil, errors.New("field of type CombinedId does not have child fields") }, } return fc, nil @@ -600,9 +601,9 @@ func (ec *executionContext) _CreateTimelineItem_id(ctx context.Context, field gr } return graphql.Null } - res := resTmp.(string) + res := resTmp.(entity.CombinedId) fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) + return ec.marshalNCombinedId2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋentityᚐCombinedId(ctx, field.Selections, res) } func (ec *executionContext) fieldContext_CreateTimelineItem_id(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { @@ -612,7 +613,7 @@ func (ec *executionContext) fieldContext_CreateTimelineItem_id(ctx context.Conte IsMethod: true, IsResolver: true, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") + return nil, errors.New("field of type CombinedId does not have child fields") }, } return fc, nil @@ -1020,9 +1021,9 @@ func (ec *executionContext) _LabelChangeTimelineItem_id(ctx context.Context, fie } return graphql.Null } - res := resTmp.(string) + res := resTmp.(entity.CombinedId) fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) + return ec.marshalNCombinedId2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋentityᚐCombinedId(ctx, field.Selections, res) } func (ec *executionContext) fieldContext_LabelChangeTimelineItem_id(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { @@ -1032,7 +1033,7 @@ func (ec *executionContext) fieldContext_LabelChangeTimelineItem_id(ctx context. IsMethod: true, IsResolver: true, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") + return nil, errors.New("field of type CombinedId does not have child fields") }, } return fc, nil @@ -1270,9 +1271,9 @@ func (ec *executionContext) _SetStatusTimelineItem_id(ctx context.Context, field } return graphql.Null } - res := resTmp.(string) + res := resTmp.(entity.CombinedId) fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) + return ec.marshalNCombinedId2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋentityᚐCombinedId(ctx, field.Selections, res) } func (ec *executionContext) fieldContext_SetStatusTimelineItem_id(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { @@ -1282,7 +1283,7 @@ func (ec *executionContext) fieldContext_SetStatusTimelineItem_id(ctx context.Co IsMethod: true, IsResolver: true, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") + return nil, errors.New("field of type CombinedId does not have child fields") }, } return fc, nil @@ -1464,9 +1465,9 @@ func (ec *executionContext) _SetTitleTimelineItem_id(ctx context.Context, field } return graphql.Null } - res := resTmp.(string) + res := resTmp.(entity.CombinedId) fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) + return ec.marshalNCombinedId2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋentityᚐCombinedId(ctx, field.Selections, res) } func (ec *executionContext) fieldContext_SetTitleTimelineItem_id(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { @@ -1476,7 +1477,7 @@ func (ec *executionContext) fieldContext_SetTitleTimelineItem_id(ctx context.Con IsMethod: true, IsResolver: true, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") + return nil, errors.New("field of type CombinedId does not have child fields") }, } return fc, nil diff --git a/api/graphql/graph/types.generated.go b/api/graphql/graph/types.generated.go index 9095c7ac..b75604ab 100644 --- a/api/graphql/graph/types.generated.go +++ b/api/graphql/graph/types.generated.go @@ -14,6 +14,7 @@ import ( "github.com/99designs/gqlgen/graphql" "github.com/MichaelMure/git-bug/api/graphql/models" "github.com/MichaelMure/git-bug/entities/bug" + "github.com/MichaelMure/git-bug/entity" "github.com/MichaelMure/git-bug/repository" "github.com/vektah/gqlparser/v2/ast" ) @@ -410,22 +411,16 @@ func (ec *executionContext) _Authored(ctx context.Context, sel ast.SelectionSet, return graphql.Null } return ec._AddCommentTimelineItem(ctx, sel, obj) - case bug.LabelChangeTimelineItem: - return ec._LabelChangeTimelineItem(ctx, sel, &obj) case *bug.LabelChangeTimelineItem: if obj == nil { return graphql.Null } return ec._LabelChangeTimelineItem(ctx, sel, obj) - case bug.SetStatusTimelineItem: - return ec._SetStatusTimelineItem(ctx, sel, &obj) case *bug.SetStatusTimelineItem: if obj == nil { return graphql.Null } return ec._SetStatusTimelineItem(ctx, sel, obj) - case bug.SetTitleTimelineItem: - return ec._SetTitleTimelineItem(ctx, sel, &obj) case *bug.SetTitleTimelineItem: if obj == nil { return graphql.Null @@ -588,6 +583,16 @@ func (ec *executionContext) marshalNColor2ᚖimageᚋcolorᚐRGBA(ctx context.Co return ec._Color(ctx, sel, v) } +func (ec *executionContext) unmarshalNCombinedId2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋentityᚐCombinedId(ctx context.Context, v interface{}) (entity.CombinedId, error) { + var res entity.CombinedId + err := res.UnmarshalGQL(v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalNCombinedId2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋentityᚐCombinedId(ctx context.Context, sel ast.SelectionSet, v entity.CombinedId) graphql.Marshaler { + return v +} + func (ec *executionContext) unmarshalNHash2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋrepositoryᚐHash(ctx context.Context, v interface{}) (repository.Hash, error) { var res repository.Hash err := res.UnmarshalGQL(v) diff --git a/api/graphql/handler.go b/api/graphql/handler.go index 03dc32e9..00141f01 100644 --- a/api/graphql/handler.go +++ b/api/graphql/handler.go @@ -1,4 +1,4 @@ -//go:generate go run gen_graphql.go +//go:generate go run github.com/99designs/gqlgen generate // Package graphql contains the root GraphQL http handler package graphql diff --git a/api/graphql/models/gen_models.go b/api/graphql/models/gen_models.go index c4e40cba..d75b3d27 100644 --- a/api/graphql/models/gen_models.go +++ b/api/graphql/models/gen_models.go @@ -161,10 +161,8 @@ type EditCommentInput struct { ClientMutationID *string `json:"clientMutationId"` // The name of the repository. If not set, the default repository is used. RepoRef *string `json:"repoRef"` - // The bug ID's prefix. - Prefix string `json:"prefix"` - // The ID of the comment to be changed. - Target string `json:"target"` + // A prefix of the CombinedId of the comment to be changed. + TargetPrefix string `json:"targetPrefix"` // The new message to be set. Message string `json:"message"` // The collection of file's hash required for the first message. diff --git a/api/graphql/resolvers/bug.go b/api/graphql/resolvers/bug.go index d17a4469..c40949fa 100644 --- a/api/graphql/resolvers/bug.go +++ b/api/graphql/resolvers/bug.go @@ -14,10 +14,6 @@ var _ graph.BugResolver = &bugResolver{} type bugResolver struct{} -func (bugResolver) ID(_ context.Context, obj models.BugWrapper) (string, error) { - return obj.Id().String(), nil -} - func (bugResolver) HumanID(_ context.Context, obj models.BugWrapper) (string, error) { return obj.Id().Human(), nil } diff --git a/api/graphql/resolvers/comment.go b/api/graphql/resolvers/comment.go index 78548156..7dddc3c8 100644 --- a/api/graphql/resolvers/comment.go +++ b/api/graphql/resolvers/comment.go @@ -6,12 +6,17 @@ import ( "github.com/MichaelMure/git-bug/api/graphql/graph" "github.com/MichaelMure/git-bug/api/graphql/models" "github.com/MichaelMure/git-bug/entities/bug" + "github.com/MichaelMure/git-bug/entity" ) var _ graph.CommentResolver = &commentResolver{} type commentResolver struct{} +func (c commentResolver) ID(ctx context.Context, obj *bug.Comment) (entity.CombinedId, error) { + return obj.CombinedId(), nil +} + func (c commentResolver) Author(_ context.Context, obj *bug.Comment) (models.IdentityWrapper, error) { return models.NewLoadedIdentity(obj.Author), nil } diff --git a/api/graphql/resolvers/mutation.go b/api/graphql/resolvers/mutation.go index 6ad81db4..f6296d63 100644 --- a/api/graphql/resolvers/mutation.go +++ b/api/graphql/resolvers/mutation.go @@ -9,7 +9,6 @@ import ( "github.com/MichaelMure/git-bug/api/graphql/models" "github.com/MichaelMure/git-bug/cache" "github.com/MichaelMure/git-bug/entities/bug" - "github.com/MichaelMure/git-bug/entity" "github.com/MichaelMure/git-bug/util/text" ) @@ -177,7 +176,12 @@ func (r mutationResolver) AddCommentAndReopen(ctx context.Context, input models. } func (r mutationResolver) EditComment(ctx context.Context, input models.EditCommentInput) (*models.EditCommentPayload, error) { - repo, b, err := r.getBug(input.RepoRef, input.Prefix) + repo, err := r.getRepo(input.RepoRef) + if err != nil { + return nil, err + } + + b, target, err := repo.ResolveComment(input.TargetPrefix) if err != nil { return nil, err } @@ -190,7 +194,7 @@ func (r mutationResolver) EditComment(ctx context.Context, input models.EditComm op, err := b.EditCommentRaw( author, time.Now().Unix(), - entity.Id(input.Target), + target, text.Cleanup(input.Message), nil, ) diff --git a/api/graphql/resolvers/operations.go b/api/graphql/resolvers/operations.go index 53c3c0bc..91194213 100644 --- a/api/graphql/resolvers/operations.go +++ b/api/graphql/resolvers/operations.go @@ -13,10 +13,6 @@ var _ graph.CreateOperationResolver = createOperationResolver{} type createOperationResolver struct{} -func (createOperationResolver) ID(_ context.Context, obj *bug.CreateOperation) (string, error) { - return obj.Id().String(), nil -} - func (createOperationResolver) Author(_ context.Context, obj *bug.CreateOperation) (models.IdentityWrapper, error) { return models.NewLoadedIdentity(obj.Author()), nil } @@ -30,10 +26,6 @@ var _ graph.AddCommentOperationResolver = addCommentOperationResolver{} type addCommentOperationResolver struct{} -func (addCommentOperationResolver) ID(_ context.Context, obj *bug.AddCommentOperation) (string, error) { - return obj.Id().String(), nil -} - func (addCommentOperationResolver) Author(_ context.Context, obj *bug.AddCommentOperation) (models.IdentityWrapper, error) { return models.NewLoadedIdentity(obj.Author()), nil } @@ -47,10 +39,6 @@ var _ graph.EditCommentOperationResolver = editCommentOperationResolver{} type editCommentOperationResolver struct{} -func (editCommentOperationResolver) ID(_ context.Context, obj *bug.EditCommentOperation) (string, error) { - return obj.Id().String(), nil -} - func (editCommentOperationResolver) Target(_ context.Context, obj *bug.EditCommentOperation) (string, error) { return obj.Target.String(), nil } @@ -68,10 +56,6 @@ var _ graph.LabelChangeOperationResolver = labelChangeOperationResolver{} type labelChangeOperationResolver struct{} -func (labelChangeOperationResolver) ID(_ context.Context, obj *bug.LabelChangeOperation) (string, error) { - return obj.Id().String(), nil -} - func (labelChangeOperationResolver) Author(_ context.Context, obj *bug.LabelChangeOperation) (models.IdentityWrapper, error) { return models.NewLoadedIdentity(obj.Author()), nil } @@ -85,10 +69,6 @@ var _ graph.SetStatusOperationResolver = setStatusOperationResolver{} type setStatusOperationResolver struct{} -func (setStatusOperationResolver) ID(_ context.Context, obj *bug.SetStatusOperation) (string, error) { - return obj.Id().String(), nil -} - func (setStatusOperationResolver) Author(_ context.Context, obj *bug.SetStatusOperation) (models.IdentityWrapper, error) { return models.NewLoadedIdentity(obj.Author()), nil } @@ -102,10 +82,6 @@ var _ graph.SetTitleOperationResolver = setTitleOperationResolver{} type setTitleOperationResolver struct{} -func (setTitleOperationResolver) ID(_ context.Context, obj *bug.SetTitleOperation) (string, error) { - return obj.Id().String(), nil -} - func (setTitleOperationResolver) Author(_ context.Context, obj *bug.SetTitleOperation) (models.IdentityWrapper, error) { return models.NewLoadedIdentity(obj.Author()), nil } diff --git a/api/graphql/resolvers/timeline.go b/api/graphql/resolvers/timeline.go index 2481784e..2d691173 100644 --- a/api/graphql/resolvers/timeline.go +++ b/api/graphql/resolvers/timeline.go @@ -7,6 +7,7 @@ import ( "github.com/MichaelMure/git-bug/api/graphql/graph" "github.com/MichaelMure/git-bug/api/graphql/models" "github.com/MichaelMure/git-bug/entities/bug" + "github.com/MichaelMure/git-bug/entity" ) var _ graph.CommentHistoryStepResolver = commentHistoryStepResolver{} @@ -22,8 +23,8 @@ var _ graph.AddCommentTimelineItemResolver = addCommentTimelineItemResolver{} type addCommentTimelineItemResolver struct{} -func (addCommentTimelineItemResolver) ID(_ context.Context, obj *bug.AddCommentTimelineItem) (string, error) { - return obj.Id().String(), nil +func (addCommentTimelineItemResolver) ID(_ context.Context, obj *bug.AddCommentTimelineItem) (entity.CombinedId, error) { + return obj.CombinedId(), nil } func (addCommentTimelineItemResolver) Author(_ context.Context, obj *bug.AddCommentTimelineItem) (models.IdentityWrapper, error) { @@ -44,8 +45,8 @@ var _ graph.CreateTimelineItemResolver = createTimelineItemResolver{} type createTimelineItemResolver struct{} -func (createTimelineItemResolver) ID(_ context.Context, obj *bug.CreateTimelineItem) (string, error) { - return obj.Id().String(), nil +func (createTimelineItemResolver) ID(_ context.Context, obj *bug.CreateTimelineItem) (entity.CombinedId, error) { + return obj.CombinedId(), nil } func (r createTimelineItemResolver) Author(_ context.Context, obj *bug.CreateTimelineItem) (models.IdentityWrapper, error) { @@ -66,8 +67,8 @@ var _ graph.LabelChangeTimelineItemResolver = labelChangeTimelineItem{} type labelChangeTimelineItem struct{} -func (labelChangeTimelineItem) ID(_ context.Context, obj *bug.LabelChangeTimelineItem) (string, error) { - return obj.Id().String(), nil +func (labelChangeTimelineItem) ID(_ context.Context, obj *bug.LabelChangeTimelineItem) (entity.CombinedId, error) { + return obj.CombinedId(), nil } func (i labelChangeTimelineItem) Author(_ context.Context, obj *bug.LabelChangeTimelineItem) (models.IdentityWrapper, error) { @@ -83,8 +84,8 @@ var _ graph.SetStatusTimelineItemResolver = setStatusTimelineItem{} type setStatusTimelineItem struct{} -func (setStatusTimelineItem) ID(_ context.Context, obj *bug.SetStatusTimelineItem) (string, error) { - return obj.Id().String(), nil +func (setStatusTimelineItem) ID(_ context.Context, obj *bug.SetStatusTimelineItem) (entity.CombinedId, error) { + return obj.CombinedId(), nil } func (i setStatusTimelineItem) Author(_ context.Context, obj *bug.SetStatusTimelineItem) (models.IdentityWrapper, error) { @@ -100,8 +101,8 @@ var _ graph.SetTitleTimelineItemResolver = setTitleTimelineItem{} type setTitleTimelineItem struct{} -func (setTitleTimelineItem) ID(_ context.Context, obj *bug.SetTitleTimelineItem) (string, error) { - return obj.Id().String(), nil +func (setTitleTimelineItem) ID(_ context.Context, obj *bug.SetTitleTimelineItem) (entity.CombinedId, error) { + return obj.CombinedId(), nil } func (i setTitleTimelineItem) Author(_ context.Context, obj *bug.SetTitleTimelineItem) (models.IdentityWrapper, error) { diff --git a/api/graphql/schema/bug.graphql b/api/graphql/schema/bug.graphql index 03aa95b8..17d3a897 100644 --- a/api/graphql/schema/bug.graphql +++ b/api/graphql/schema/bug.graphql @@ -1,5 +1,7 @@ """Represents a comment on a bug.""" type Comment implements Authored { + id: CombinedId! + """The author of this comment.""" author: Identity! @@ -29,7 +31,7 @@ enum Status { type Bug implements Authored { """The identifier for this bug""" - id: String! + id: ID! """The human version (truncated) identifier for this bug""" humanId: String! status: Status! diff --git a/api/graphql/schema/identity.graphql b/api/graphql/schema/identity.graphql index 93154a90..c910ea55 100644 --- a/api/graphql/schema/identity.graphql +++ b/api/graphql/schema/identity.graphql @@ -1,7 +1,7 @@ """Represents an identity""" type Identity { """The identifier for this identity""" - id: String! + id: ID! """The human version (truncated) identifier for this identity""" humanId: String! """The name of the person, if known.""" diff --git a/api/graphql/schema/mutations.graphql b/api/graphql/schema/mutations.graphql index 078dc214..be6a0115 100644 --- a/api/graphql/schema/mutations.graphql +++ b/api/graphql/schema/mutations.graphql @@ -95,10 +95,8 @@ input EditCommentInput { 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 ID of the comment to be changed.""" - target: String! + """A prefix of the CombinedId of the comment to be changed.""" + targetPrefix: String! """The new message to be set.""" message: String! """The collection of file's hash required for the first message.""" diff --git a/api/graphql/schema/operations.graphql b/api/graphql/schema/operations.graphql index 18e0929c..8e198753 100644 --- a/api/graphql/schema/operations.graphql +++ b/api/graphql/schema/operations.graphql @@ -1,7 +1,7 @@ """An operation applied to a bug.""" interface Operation { """The identifier of the operation""" - id: String! + id: ID! """The operations author.""" author: Identity! """The datetime when this operation was issued.""" @@ -28,7 +28,7 @@ type OperationEdge { type CreateOperation implements Operation & Authored { """The identifier of the operation""" - id: String! + id: ID! """The author of this object.""" author: Identity! """The datetime when this operation was issued.""" @@ -41,7 +41,7 @@ type CreateOperation implements Operation & Authored { type SetTitleOperation implements Operation & Authored { """The identifier of the operation""" - id: String! + id: ID! """The author of this object.""" author: Identity! """The datetime when this operation was issued.""" @@ -53,7 +53,7 @@ type SetTitleOperation implements Operation & Authored { type AddCommentOperation implements Operation & Authored { """The identifier of the operation""" - id: String! + id: ID! """The author of this object.""" author: Identity! """The datetime when this operation was issued.""" @@ -65,7 +65,7 @@ type AddCommentOperation implements Operation & Authored { type EditCommentOperation implements Operation & Authored { """The identifier of the operation""" - id: String! + id: ID! """The author of this object.""" author: Identity! """The datetime when this operation was issued.""" @@ -78,7 +78,7 @@ type EditCommentOperation implements Operation & Authored { type SetStatusOperation implements Operation & Authored { """The identifier of the operation""" - id: String! + id: ID! """The author of this object.""" author: Identity! """The datetime when this operation was issued.""" @@ -89,7 +89,7 @@ type SetStatusOperation implements Operation & Authored { type LabelChangeOperation implements Operation & Authored { """The identifier of the operation""" - id: String! + id: ID! """The author of this object.""" author: Identity! """The datetime when this operation was issued.""" diff --git a/api/graphql/schema/timeline.graphql b/api/graphql/schema/timeline.graphql index 12462aa3..b7ab5ca8 100644 --- a/api/graphql/schema/timeline.graphql +++ b/api/graphql/schema/timeline.graphql @@ -1,7 +1,7 @@ """An item in the timeline of events""" interface TimelineItem { """The identifier of the source operation""" - id: String! + id: CombinedId! } """CommentHistoryStep hold one version of a message in the history""" @@ -31,7 +31,7 @@ type TimelineItemEdge { """CreateTimelineItem is a TimelineItem that represent the creation of a bug and its message edition history""" type CreateTimelineItem implements TimelineItem & Authored { """The identifier of the source operation""" - id: String! + id: CombinedId! author: Identity! message: String! messageIsEmpty: Boolean! @@ -45,7 +45,7 @@ type CreateTimelineItem implements TimelineItem & Authored { """AddCommentTimelineItem is a TimelineItem that represent a Comment and its edition history""" type AddCommentTimelineItem implements TimelineItem & Authored { """The identifier of the source operation""" - id: String! + id: CombinedId! author: Identity! message: String! messageIsEmpty: Boolean! @@ -59,7 +59,7 @@ type AddCommentTimelineItem implements TimelineItem & Authored { """LabelChangeTimelineItem is a TimelineItem that represent a change in the labels of a bug""" type LabelChangeTimelineItem implements TimelineItem & Authored { """The identifier of the source operation""" - id: String! + id: CombinedId! author: Identity! date: Time! added: [Label!]! @@ -69,7 +69,7 @@ type LabelChangeTimelineItem implements TimelineItem & Authored { """SetStatusTimelineItem is a TimelineItem that represent a change in the status of a bug""" type SetStatusTimelineItem implements TimelineItem & Authored { """The identifier of the source operation""" - id: String! + id: CombinedId! author: Identity! date: Time! status: Status! @@ -78,7 +78,7 @@ type SetStatusTimelineItem implements TimelineItem & Authored { """LabelChangeTimelineItem is a TimelineItem that represent a change in the title of a bug""" type SetTitleTimelineItem implements TimelineItem & Authored { """The identifier of the source operation""" - id: String! + id: CombinedId! author: Identity! date: Time! title: String! diff --git a/api/graphql/schema/types.graphql b/api/graphql/schema/types.graphql index 0182885e..f4284b2d 100644 --- a/api/graphql/schema/types.graphql +++ b/api/graphql/schema/types.graphql @@ -1,3 +1,4 @@ +scalar CombinedId scalar Time scalar Hash diff --git a/api/graphql/tools.go b/api/graphql/tools.go new file mode 100644 index 00000000..863672ec --- /dev/null +++ b/api/graphql/tools.go @@ -0,0 +1,8 @@ +//go:build tools +// +build tools + +package graphql + +import ( + _ "github.com/99designs/gqlgen" +) diff --git a/api/http/git_file_handler.go b/api/http/git_file_handler.go index 0bc0fd0c..b5676970 100644 --- a/api/http/git_file_handler.go +++ b/api/http/git_file_handler.go @@ -47,7 +47,7 @@ func (gfh *gitFileHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) { return } - // TODO: this mean that the whole file will he buffered in memory + // TODO: this mean that the whole file will be buffered in memory // This can be a problem for big files. There might be a way around // that by implementing a io.ReadSeeker that would read and discard // data when a seek is called. diff --git a/bridge/github/export_test.go b/bridge/github/export_test.go index 93cc47c0..de2d3f34 100644 --- a/bridge/github/export_test.go +++ b/bridge/github/export_test.go @@ -16,6 +16,7 @@ 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/entity" "github.com/MichaelMure/git-bug/entity/dag" "github.com/MichaelMure/git-bug/repository" "github.com/MichaelMure/git-bug/util/interrupt" @@ -66,13 +67,15 @@ func testCases(t *testing.T, repo *cache.RepoCache) []*testCase { bugWithCommentEditions, createOp, err := repo.NewBug("bug with comments editions", "new bug") require.NoError(t, err) - _, err = bugWithCommentEditions.EditComment(createOp.Id(), "first comment edited") + _, err = bugWithCommentEditions.EditComment( + entity.CombineIds(bugWithCommentEditions.Id(), createOp.Id()), "first comment edited") require.NoError(t, err) commentOp, err := bugWithCommentEditions.AddComment("first comment") require.NoError(t, err) - _, err = bugWithCommentEditions.EditComment(commentOp.Id(), "first comment edited") + _, err = bugWithCommentEditions.EditComment( + entity.CombineIds(bugWithCommentEditions.Id(), commentOp.Id()), "first comment edited") require.NoError(t, err) // bug status changed diff --git a/bridge/github/import.go b/bridge/github/import.go index 722bf256..5b2bf54b 100644 --- a/bridge/github/import.go +++ b/bridge/github/import.go @@ -405,6 +405,7 @@ func (gi *githubImporter) ensureCommentEdit(ctx context.Context, repo *cache.Rep if err != nil { return err } + // check if the comment edition already exist _, err = b.ResolveOperationWithMetadata(metaKeyGithubId, parseId(edit.Id)) if err == nil { return nil @@ -428,7 +429,7 @@ func (gi *githubImporter) ensureCommentEdit(ctx context.Context, repo *cache.Rep op, err := b.EditCommentRaw( editor, edit.CreatedAt.Unix(), - target, + entity.CombineIds(b.Id(), target), text.Cleanup(string(*edit.Diff)), map[string]string{ metaKeyGithubId: parseId(edit.Id), diff --git a/bridge/gitlab/export_test.go b/bridge/gitlab/export_test.go index b48254e6..e9f3ae75 100644 --- a/bridge/gitlab/export_test.go +++ b/bridge/gitlab/export_test.go @@ -11,6 +11,7 @@ import ( "github.com/xanzy/go-gitlab" + "github.com/MichaelMure/git-bug/entity" "github.com/MichaelMure/git-bug/entity/dag" "github.com/stretchr/testify/require" @@ -63,13 +64,15 @@ func testCases(t *testing.T, repo *cache.RepoCache) []*testCase { bugWithCommentEditions, createOp, err := repo.NewBug("bug with comments editions", "new bug") require.NoError(t, err) - _, err = bugWithCommentEditions.EditComment(createOp.Id(), "first comment edited") + _, err = bugWithCommentEditions.EditComment( + entity.CombineIds(bugWithCommentEditions.Id(), createOp.Id()), "first comment edited") require.NoError(t, err) commentOp, err := bugWithCommentEditions.AddComment("first comment") require.NoError(t, err) - _, err = bugWithCommentEditions.EditComment(commentOp.Id(), "first comment edited") + _, err = bugWithCommentEditions.EditComment( + entity.CombineIds(bugWithCommentEditions.Id(), commentOp.Id()), "first comment edited") require.NoError(t, err) // bug status changed diff --git a/bridge/gitlab/import.go b/bridge/gitlab/import.go index 8228c4a0..cf6b5ca6 100644 --- a/bridge/gitlab/import.go +++ b/bridge/gitlab/import.go @@ -55,7 +55,6 @@ func (gi *gitlabImporter) Init(_ context.Context, repo *cache.RepoCache, conf co // ImportAll iterate over all the configured repository issues (notes) and ensure the creation // of the missing issues / comments / label events / title changes ... func (gi *gitlabImporter) ImportAll(ctx context.Context, repo *cache.RepoCache, since time.Time) (<-chan core.ImportResult, error) { - out := make(chan core.ImportResult) gi.out = out @@ -150,7 +149,6 @@ func (gi *gitlabImporter) ensureIssue(repo *cache.RepoCache, issue *gitlab.Issue } func (gi *gitlabImporter) ensureIssueEvent(repo *cache.RepoCache, b *cache.BugCache, issue *gitlab.Issue, event Event) error { - id, errResolve := b.ResolveOperationWithMetadata(metaKeyGitlabId, event.ID()) if errResolve != nil && errResolve != cache.ErrNoMatchingOp { return errResolve @@ -205,13 +203,14 @@ func (gi *gitlabImporter) ensureIssueEvent(repo *cache.RepoCache, b *cache.BugCa // since gitlab doesn't provide the issue history // we should check for "changed the description" notes and compare issue texts // TODO: Check only one time and ignore next 'description change' within one issue - if errResolve == cache.ErrNoMatchingOp && issue.Description != firstComment.Message { + cleanedDesc := text.Cleanup(issue.Description) + if errResolve == cache.ErrNoMatchingOp && cleanedDesc != firstComment.Message { // comment edition op, err := b.EditCommentRaw( author, event.(NoteEvent).UpdatedAt.Unix(), - firstComment.Id(), - text.Cleanup(issue.Description), + firstComment.CombinedId(), + cleanedDesc, map[string]string{ metaKeyGitlabId: event.ID(), }, @@ -249,7 +248,7 @@ func (gi *gitlabImporter) ensureIssueEvent(repo *cache.RepoCache, b *cache.BugCa // if comment was already exported // search for last comment update - comment, err := b.Snapshot().SearchComment(id) + comment, err := b.Snapshot().SearchCommentByOpId(id) if err != nil { return err } @@ -260,7 +259,7 @@ func (gi *gitlabImporter) ensureIssueEvent(repo *cache.RepoCache, b *cache.BugCa op, err := b.EditCommentRaw( author, event.(NoteEvent).UpdatedAt.Unix(), - comment.Id(), + comment.CombinedId(), cleanText, nil, ) diff --git a/bridge/jira/import.go b/bridge/jira/import.go index c297abcf..8043acf4 100644 --- a/bridge/jira/import.go +++ b/bridge/jira/import.go @@ -270,8 +270,7 @@ func (ji *jiraImporter) ensureComment(repo *cache.RepoCache, b *cache.BugCache, return err } - targetOpID, err := b.ResolveOperationWithMetadata( - metaKeyJiraId, item.ID) + targetOpID, err := b.ResolveOperationWithMetadata(metaKeyJiraId, item.ID) if err != nil && err != cache.ErrNoMatchingOp { return err } @@ -334,7 +333,7 @@ func (ji *jiraImporter) ensureComment(repo *cache.RepoCache, b *cache.BugCache, op, err := b.EditCommentRaw( editor, item.Updated.Unix(), - targetOpID, + entity.CombineIds(b.Id(), targetOpID), text.Cleanup(item.Body), map[string]string{ metaKeyJiraId: derivedID, diff --git a/cache/bug_cache.go b/cache/bug_cache.go index e03b27ff..6caaeb11 100644 --- a/cache/bug_cache.go +++ b/cache/bug_cache.go @@ -209,7 +209,7 @@ func (c *BugCache) EditCreateCommentRaw(author *IdentityCache, unixTime int64, b return op, c.notifyUpdated() } -func (c *BugCache) EditComment(target entity.Id, message string) (*bug.EditCommentOperation, error) { +func (c *BugCache) EditComment(target entity.CombinedId, message string) (*bug.EditCommentOperation, error) { author, err := c.repoCache.GetUserIdentity() if err != nil { return nil, err @@ -218,9 +218,14 @@ func (c *BugCache) EditComment(target entity.Id, message string) (*bug.EditComme return c.EditCommentRaw(author, time.Now().Unix(), target, message, nil) } -func (c *BugCache) EditCommentRaw(author *IdentityCache, unixTime int64, target entity.Id, message string, metadata map[string]string) (*bug.EditCommentOperation, error) { +func (c *BugCache) EditCommentRaw(author *IdentityCache, unixTime int64, target entity.CombinedId, message string, metadata map[string]string) (*bug.EditCommentOperation, error) { + comment, err := c.Snapshot().SearchComment(target) + if err != nil { + return nil, err + } + c.mu.Lock() - op, err := bug.EditComment(c.bug, author.Identity, unixTime, target, message, nil, metadata) + op, err := bug.EditComment(c.bug, author.Identity, unixTime, comment.TargetId(), message, nil, metadata) c.mu.Unlock() if err != nil { return nil, err diff --git a/cache/repo_cache_bug.go b/cache/repo_cache_bug.go index 9843f9d9..226c4c13 100644 --- a/cache/repo_cache_bug.go +++ b/cache/repo_cache_bug.go @@ -263,7 +263,7 @@ func (c *RepoCache) resolveBugMatcher(f func(*BugExcerpt) bool) (entity.Id, erro // ResolveComment search for a Bug/Comment combination matching the merged // bug/comment Id prefix. Returns the Bug containing the Comment and the Comment's // Id. -func (c *RepoCache) ResolveComment(prefix string) (*BugCache, entity.Id, error) { +func (c *RepoCache) ResolveComment(prefix string) (*BugCache, entity.CombinedId, error) { bugPrefix, _ := entity.SeparateIds(prefix) bugCandidate := make([]entity.Id, 0, 5) @@ -277,7 +277,7 @@ func (c *RepoCache) ResolveComment(prefix string) (*BugCache, entity.Id, error) c.muBug.RUnlock() matchingBugIds := make([]entity.Id, 0, 5) - matchingCommentId := entity.UnsetId + matchingCommentId := entity.UnsetCombinedId var matchingBug *BugCache // search for matching comments @@ -286,22 +286,22 @@ func (c *RepoCache) ResolveComment(prefix string) (*BugCache, entity.Id, error) for _, bugId := range bugCandidate { b, err := c.ResolveBug(bugId) if err != nil { - return nil, entity.UnsetId, err + return nil, entity.UnsetCombinedId, err } for _, comment := range b.Snapshot().Comments { - if comment.Id().HasPrefix(prefix) { + if comment.TargetId().HasPrefix(prefix) { matchingBugIds = append(matchingBugIds, bugId) matchingBug = b - matchingCommentId = comment.Id() + matchingCommentId = comment.CombinedId() } } } if len(matchingBugIds) > 1 { - return nil, entity.UnsetId, entity.NewErrMultipleMatch("bug/comment", matchingBugIds) + return nil, entity.UnsetCombinedId, entity.NewErrMultipleMatch("bug/comment", matchingBugIds) } else if len(matchingBugIds) == 0 { - return nil, entity.UnsetId, errors.New("comment doesn't exist") + return nil, entity.UnsetCombinedId, errors.New("comment doesn't exist") } return matchingBug, matchingCommentId, nil diff --git a/commands/comment.go b/commands/comment.go index b4b4628b..7cab447c 100644 --- a/commands/comment.go +++ b/commands/comment.go @@ -41,7 +41,7 @@ func runComment(env *Env, args []string) error { } env.out.Printf("Author: %s\n", colors.Magenta(comment.Author.DisplayName())) - env.out.Printf("Id: %s\n", colors.Cyan(comment.Id().Human())) + env.out.Printf("Id: %s\n", colors.Cyan(comment.CombinedId().Human())) env.out.Printf("Date: %s\n\n", comment.FormatTime()) env.out.Println(text.LeftPadLines(comment.Message, 4)) } diff --git a/commands/show.go b/commands/show.go index d145ffe7..1491372e 100644 --- a/commands/show.go +++ b/commands/show.go @@ -163,7 +163,7 @@ func showDefaultFormatter(env *Env, snapshot *bug.Snapshot) error { var message string env.out.Printf("%s%s #%d %s <%s>\n\n", indent, - comment.Id().Human(), + comment.CombinedId().Human(), i, comment.Author.DisplayName(), comment.Author.Email(), @@ -207,8 +207,8 @@ type JSONComment struct { func NewJSONComment(comment bug.Comment) JSONComment { return JSONComment{ - Id: comment.Id().String(), - HumanId: comment.Id().Human(), + Id: comment.CombinedId().String(), + HumanId: comment.CombinedId().Human(), Author: NewJSONIdentity(comment.Author), Message: comment.Message, } diff --git a/entities/bug/comment.go b/entities/bug/comment.go index fcf501ab..7835c5a8 100644 --- a/entities/bug/comment.go +++ b/entities/bug/comment.go @@ -11,34 +11,41 @@ import ( // Comment represent a comment in a Bug type Comment struct { - // id should be the result of entity.CombineIds with the Bug id and the id + // combinedId should be the result of entity.CombineIds with the Bug id and the id // of the Operation that created the comment - id entity.Id + combinedId entity.CombinedId + + // targetId is the Id of the Operation that originally created that Comment + targetId entity.Id + Author identity.Interface Message string Files []repository.Hash // Creation time of the comment. // Should be used only for human display, never for ordering as we can't rely on it in a distributed system. - UnixTime timestamp.Timestamp + unixTime timestamp.Timestamp } -// Id return the Comment identifier -func (c Comment) Id() entity.Id { - if c.id == "" { +func (c Comment) CombinedId() entity.CombinedId { + if c.combinedId == "" { // simply panic as it would be a coding error (no id provided at construction) - panic("no id") + panic("no combined id") } - return c.id + return c.combinedId +} + +func (c Comment) TargetId() entity.Id { + return c.targetId } -// FormatTimeRel format the UnixTime of the comment for human consumption +// FormatTimeRel format the unixTime of the comment for human consumption func (c Comment) FormatTimeRel() string { - return humanize.Time(c.UnixTime.Time()) + return humanize.Time(c.unixTime.Time()) } func (c Comment) FormatTime() string { - return c.UnixTime.Time().Format("Mon Jan 2 15:04:05 2006 +0200") + return c.unixTime.Time().Format("Mon Jan 2 15:04:05 2006 +0200") } // IsAuthored is a sign post method for gqlgen diff --git a/entities/bug/op_add_comment.go b/entities/bug/op_add_comment.go index 2e6a39f9..b049ef16 100644 --- a/entities/bug/op_add_comment.go +++ b/entities/bug/op_add_comment.go @@ -30,12 +30,15 @@ func (op *AddCommentOperation) Apply(snapshot *Snapshot) { snapshot.addActor(op.Author()) snapshot.addParticipant(op.Author()) + opId := op.Id() + comment := Comment{ - id: entity.CombineIds(snapshot.Id(), op.Id()), - Message: op.Message, - Author: op.Author(), - Files: op.Files, - UnixTime: timestamp.Timestamp(op.UnixTime), + combinedId: entity.CombineIds(snapshot.Id(), opId), + targetId: opId, + Message: op.Message, + Author: op.Author(), + Files: op.Files, + unixTime: timestamp.Timestamp(op.UnixTime), } snapshot.Comments = append(snapshot.Comments, comment) @@ -71,7 +74,7 @@ func NewAddCommentOp(author identity.Interface, unixTime int64, message string, } } -// AddCommentTimelineItem hold a comment in the timeline +// AddCommentTimelineItem replace a AddComment operation in the Timeline and hold its edition history type AddCommentTimelineItem struct { CommentTimelineItem } diff --git a/entities/bug/op_create.go b/entities/bug/op_create.go index fdfa131b..2afea406 100644 --- a/entities/bug/op_create.go +++ b/entities/bug/op_create.go @@ -32,7 +32,9 @@ func (op *CreateOperation) Apply(snapshot *Snapshot) { return } - snapshot.id = op.Id() + // the Id of the Bug/Snapshot is the Id of the first Operation: CreateOperation + opId := op.Id() + snapshot.id = opId snapshot.addActor(op.Author()) snapshot.addParticipant(op.Author()) @@ -40,10 +42,11 @@ func (op *CreateOperation) Apply(snapshot *Snapshot) { snapshot.Title = op.Title comment := Comment{ - id: entity.CombineIds(snapshot.Id(), op.Id()), - Message: op.Message, - Author: op.Author(), - UnixTime: timestamp.Timestamp(op.UnixTime), + combinedId: entity.CombineIds(snapshot.id, opId), + targetId: opId, + Message: op.Message, + Author: op.Author(), + unixTime: timestamp.Timestamp(op.UnixTime), } snapshot.Comments = []Comment{comment} diff --git a/entities/bug/op_create_test.go b/entities/bug/op_create_test.go index f2c9e675..e534162b 100644 --- a/entities/bug/op_create_test.go +++ b/entities/bug/op_create_test.go @@ -6,55 +6,37 @@ import ( "github.com/stretchr/testify/require" + "github.com/MichaelMure/git-bug/entities/common" "github.com/MichaelMure/git-bug/entities/identity" "github.com/MichaelMure/git-bug/entity" "github.com/MichaelMure/git-bug/entity/dag" "github.com/MichaelMure/git-bug/repository" - "github.com/MichaelMure/git-bug/util/timestamp" ) func TestCreate(t *testing.T) { - snapshot := Snapshot{} - - repo := repository.NewMockRepoClock() + repo := repository.NewMockRepo() rene, err := identity.NewIdentity(repo, "René Descartes", "rene@descartes.fr") require.NoError(t, err) - unix := time.Now().Unix() - - create := NewCreateOp(rene, unix, "title", "message", nil) - - create.Apply(&snapshot) - - id := create.Id() - require.NoError(t, id.Validate()) - - comment := Comment{ - id: entity.CombineIds(create.Id(), create.Id()), - Author: rene, - Message: "message", - UnixTime: timestamp.Timestamp(create.UnixTime), - } - - expected := Snapshot{ - id: create.Id(), - Title: "title", - Comments: []Comment{ - comment, - }, - Author: rene, - Participants: []identity.Interface{rene}, - Actors: []identity.Interface{rene}, - CreateTime: create.Time(), - Timeline: []TimelineItem{ - &CreateTimelineItem{ - CommentTimelineItem: NewCommentTimelineItem(comment), - }, - }, - } + b, op, err := Create(rene, time.Now().Unix(), "title", "message", nil, nil) + require.NoError(t, err) - require.Equal(t, expected, snapshot) + require.Equal(t, "title", op.Title) + require.Equal(t, "message", op.Message) + + // Create generate the initial operation and create a new timeline item + snap := b.Compile() + require.Equal(t, common.OpenStatus, snap.Status) + require.Equal(t, rene, snap.Author) + require.Equal(t, "title", snap.Title) + require.Len(t, snap.Operations, 1) + require.Equal(t, op, snap.Operations[0]) + + require.Len(t, snap.Timeline, 1) + require.Equal(t, entity.CombineIds(b.Id(), op.Id()), snap.Timeline[0].CombinedId()) + require.Equal(t, rene, snap.Timeline[0].(*CreateTimelineItem).Author) + require.Equal(t, "message", snap.Timeline[0].(*CreateTimelineItem).Message) } func TestCreateSerialize(t *testing.T) { diff --git a/entities/bug/op_edit_comment.go b/entities/bug/op_edit_comment.go index 41079f45..b0897b0a 100644 --- a/entities/bug/op_edit_comment.go +++ b/entities/bug/op_edit_comment.go @@ -33,12 +33,12 @@ func (op *EditCommentOperation) Apply(snapshot *Snapshot) { // Todo: currently any message can be edited, even by a different author // crypto signature are needed. - // Recreate the Comment Id to match on - commentId := entity.CombineIds(snapshot.Id(), op.Target) + // Recreate the combined Id to match on + combinedId := entity.CombineIds(snapshot.Id(), op.Target) var target TimelineItem for i, item := range snapshot.Timeline { - if item.Id() == commentId { + if item.CombinedId() == combinedId { target = snapshot.Timeline[i] break } @@ -50,10 +50,11 @@ func (op *EditCommentOperation) Apply(snapshot *Snapshot) { } comment := Comment{ - id: commentId, - Message: op.Message, - Files: op.Files, - UnixTime: timestamp.Timestamp(op.UnixTime), + combinedId: combinedId, + targetId: op.Target, + Message: op.Message, + Files: op.Files, + unixTime: timestamp.Timestamp(op.UnixTime), } switch target := target.(type) { @@ -72,7 +73,7 @@ func (op *EditCommentOperation) Apply(snapshot *Snapshot) { // Updating the corresponding comment for i := range snapshot.Comments { - if snapshot.Comments[i].Id() == commentId { + if snapshot.Comments[i].CombinedId() == combinedId { snapshot.Comments[i].Message = op.Message snapshot.Comments[i].Files = op.Files break diff --git a/entities/bug/op_label_change.go b/entities/bug/op_label_change.go index 45441f7c..76b2ebef 100644 --- a/entities/bug/op_label_change.go +++ b/entities/bug/op_label_change.go @@ -59,12 +59,14 @@ AddLoop: return string(snapshot.Labels[i]) < string(snapshot.Labels[j]) }) + id := op.Id() item := &LabelChangeTimelineItem{ - id: op.Id(), - Author: op.Author(), - UnixTime: timestamp.Timestamp(op.UnixTime), - Added: op.Added, - Removed: op.Removed, + // id: id, + combinedId: entity.CombineIds(snapshot.Id(), id), + Author: op.Author(), + UnixTime: timestamp.Timestamp(op.UnixTime), + Added: op.Added, + Removed: op.Removed, } snapshot.Timeline = append(snapshot.Timeline, item) @@ -103,19 +105,20 @@ func NewLabelChangeOperation(author identity.Interface, unixTime int64, added, r } type LabelChangeTimelineItem struct { - id entity.Id - Author identity.Interface - UnixTime timestamp.Timestamp - Added []Label - Removed []Label + // id entity.Id + combinedId entity.CombinedId + Author identity.Interface + UnixTime timestamp.Timestamp + Added []Label + Removed []Label } -func (l LabelChangeTimelineItem) Id() entity.Id { - return l.id +func (l LabelChangeTimelineItem) CombinedId() entity.CombinedId { + return l.combinedId } // IsAuthored is a sign post method for gqlgen -func (l LabelChangeTimelineItem) IsAuthored() {} +func (l *LabelChangeTimelineItem) IsAuthored() {} // ChangeLabels is a convenience function to change labels on a bug func ChangeLabels(b Interface, author identity.Interface, unixTime int64, add, remove []string, metadata map[string]string) ([]LabelChangeResult, *LabelChangeOperation, error) { @@ -180,7 +183,7 @@ func ChangeLabels(b Interface, author identity.Interface, unixTime int64, add, r } // ForceChangeLabels is a convenience function to apply the operation -// The difference with ChangeLabels is that no checks of deduplications are done. You are entirely +// The difference with ChangeLabels is that no checks for deduplication are done. You are entirely // responsible for what you are doing. In the general case, you want to use ChangeLabels instead. // The intended use of this function is to allow importers to create legal but unexpected label changes, // like removing a label with no information of when it was added before. diff --git a/entities/bug/op_set_status.go b/entities/bug/op_set_status.go index cf17901a..23be59a0 100644 --- a/entities/bug/op_set_status.go +++ b/entities/bug/op_set_status.go @@ -26,11 +26,13 @@ func (op *SetStatusOperation) Apply(snapshot *Snapshot) { snapshot.Status = op.Status snapshot.addActor(op.Author()) + id := op.Id() item := &SetStatusTimelineItem{ - id: op.Id(), - Author: op.Author(), - UnixTime: timestamp.Timestamp(op.UnixTime), - Status: op.Status, + // id: id, + combinedId: entity.CombineIds(snapshot.Id(), id), + Author: op.Author(), + UnixTime: timestamp.Timestamp(op.UnixTime), + Status: op.Status, } snapshot.Timeline = append(snapshot.Timeline, item) @@ -56,18 +58,19 @@ func NewSetStatusOp(author identity.Interface, unixTime int64, status common.Sta } type SetStatusTimelineItem struct { - id entity.Id - Author identity.Interface - UnixTime timestamp.Timestamp - Status common.Status + // id entity.Id + combinedId entity.CombinedId + Author identity.Interface + UnixTime timestamp.Timestamp + Status common.Status } -func (s SetStatusTimelineItem) Id() entity.Id { - return s.id +func (s SetStatusTimelineItem) CombinedId() entity.CombinedId { + return s.combinedId } // IsAuthored is a sign post method for gqlgen -func (s SetStatusTimelineItem) IsAuthored() {} +func (s *SetStatusTimelineItem) IsAuthored() {} // Open is a convenience function to change a bugs state to Open func Open(b Interface, author identity.Interface, unixTime int64, metadata map[string]string) (*SetStatusOperation, error) { diff --git a/entities/bug/op_set_title.go b/entities/bug/op_set_title.go index 75efd08e..e9526b64 100644 --- a/entities/bug/op_set_title.go +++ b/entities/bug/op_set_title.go @@ -28,12 +28,14 @@ func (op *SetTitleOperation) Apply(snapshot *Snapshot) { snapshot.Title = op.Title snapshot.addActor(op.Author()) + id := op.Id() item := &SetTitleTimelineItem{ - id: op.Id(), - Author: op.Author(), - UnixTime: timestamp.Timestamp(op.UnixTime), - Title: op.Title, - Was: op.Was, + id: id, + combinedId: entity.CombineIds(snapshot.Id(), id), + Author: op.Author(), + UnixTime: timestamp.Timestamp(op.UnixTime), + Title: op.Title, + Was: op.Was, } snapshot.Timeline = append(snapshot.Timeline, item) @@ -68,19 +70,24 @@ func NewSetTitleOp(author identity.Interface, unixTime int64, title string, was } type SetTitleTimelineItem struct { - id entity.Id - Author identity.Interface - UnixTime timestamp.Timestamp - Title string - Was string + id entity.Id + combinedId entity.CombinedId + Author identity.Interface + UnixTime timestamp.Timestamp + Title string + Was string } func (s SetTitleTimelineItem) Id() entity.Id { return s.id } +func (s SetTitleTimelineItem) CombinedId() entity.CombinedId { + return s.combinedId +} + // IsAuthored is a sign post method for gqlgen -func (s SetTitleTimelineItem) IsAuthored() {} +func (s *SetTitleTimelineItem) IsAuthored() {} // SetTitle is a convenience function to change a bugs title func SetTitle(b Interface, author identity.Interface, unixTime int64, title string, metadata map[string]string) (*SetTitleOperation, error) { diff --git a/entities/bug/snapshot.go b/entities/bug/snapshot.go index 442496f7..333fe207 100644 --- a/entities/bug/snapshot.go +++ b/entities/bug/snapshot.go @@ -58,9 +58,9 @@ func (snap *Snapshot) GetCreateMetadata(key string) (string, bool) { } // SearchTimelineItem will search in the timeline for an item matching the given hash -func (snap *Snapshot) SearchTimelineItem(id entity.Id) (TimelineItem, error) { +func (snap *Snapshot) SearchTimelineItem(id entity.CombinedId) (TimelineItem, error) { for i := range snap.Timeline { - if snap.Timeline[i].Id() == id { + if snap.Timeline[i].CombinedId() == id { return snap.Timeline[i], nil } } @@ -68,15 +68,26 @@ func (snap *Snapshot) SearchTimelineItem(id entity.Id) (TimelineItem, error) { return nil, fmt.Errorf("timeline item not found") } -// SearchComment will search for a comment matching the given hash -func (snap *Snapshot) SearchComment(id entity.Id) (*Comment, error) { +// SearchComment will search for a comment matching the given id +func (snap *Snapshot) SearchComment(id entity.CombinedId) (*Comment, error) { for _, c := range snap.Comments { - if c.id == id { + if c.combinedId == id { return &c, nil } } - return nil, fmt.Errorf("comment item not found") + return nil, fmt.Errorf("comment not found") +} + +// SearchCommentByOpId will search for a comment generated by the given operation Id +func (snap *Snapshot) SearchCommentByOpId(id entity.Id) (*Comment, error) { + for _, c := range snap.Comments { + if c.targetId == id { + return &c, nil + } + } + + return nil, fmt.Errorf("comment not found") } // append the operation author to the actors list diff --git a/entities/bug/timeline.go b/entities/bug/timeline.go index d7f042db..84ece262 100644 --- a/entities/bug/timeline.go +++ b/entities/bug/timeline.go @@ -10,8 +10,8 @@ import ( ) type TimelineItem interface { - // Id return the identifier of the item - Id() entity.Id + // CombinedId returns the global identifier of the item + CombinedId() entity.CombinedId } // CommentHistoryStep hold one version of a message in the history @@ -26,46 +26,46 @@ type CommentHistoryStep struct { // CommentTimelineItem is a TimelineItem that holds a Comment and its edition history type CommentTimelineItem struct { - // id should be the same as in Comment - id entity.Id - Author identity.Interface - Message string - Files []repository.Hash - CreatedAt timestamp.Timestamp - LastEdit timestamp.Timestamp - History []CommentHistoryStep + combinedId entity.CombinedId + Author identity.Interface + Message string + Files []repository.Hash + CreatedAt timestamp.Timestamp + LastEdit timestamp.Timestamp + History []CommentHistoryStep } func NewCommentTimelineItem(comment Comment) CommentTimelineItem { return CommentTimelineItem{ - id: comment.id, - Author: comment.Author, - Message: comment.Message, - Files: comment.Files, - CreatedAt: comment.UnixTime, - LastEdit: comment.UnixTime, + // id: comment.id, + combinedId: comment.combinedId, + Author: comment.Author, + Message: comment.Message, + Files: comment.Files, + CreatedAt: comment.unixTime, + LastEdit: comment.unixTime, History: []CommentHistoryStep{ { Message: comment.Message, - UnixTime: comment.UnixTime, + UnixTime: comment.unixTime, }, }, } } -func (c *CommentTimelineItem) Id() entity.Id { - return c.id +func (c *CommentTimelineItem) CombinedId() entity.CombinedId { + return c.combinedId } // Append will append a new comment in the history and update the other values func (c *CommentTimelineItem) Append(comment Comment) { c.Message = comment.Message c.Files = comment.Files - c.LastEdit = comment.UnixTime + c.LastEdit = comment.unixTime c.History = append(c.History, CommentHistoryStep{ Author: comment.Author, Message: comment.Message, - UnixTime: comment.UnixTime, + UnixTime: comment.unixTime, }) } diff --git a/entity/id.go b/entity/id.go index 8f3dc25b..49398da8 100644 --- a/entity/id.go +++ b/entity/id.go @@ -79,21 +79,3 @@ func (i Id) Validate() error { } return nil } - -/* - * Sorting - */ - -type Alphabetical []Id - -func (a Alphabetical) Len() int { - return len(a) -} - -func (a Alphabetical) Less(i, j int) bool { - return a[i] < a[j] -} - -func (a Alphabetical) Swap(i, j int) { - a[i], a[j] = a[j], a[i] -} diff --git a/entity/id_interleaved.go b/entity/id_interleaved.go index 0ce2ba00..28c59a42 100644 --- a/entity/id_interleaved.go +++ b/entity/id_interleaved.go @@ -1,13 +1,91 @@ package entity import ( + "fmt" + "io" "strings" + + "github.com/pkg/errors" ) +const UnsetCombinedId = CombinedId("unset") + +// CombinedId is an Id holding information from both a primary Id and a secondary Id. +// While it looks like a regular Id, do not just cast from one to another. +// Instead, use CombineIds and SeparateIds to create it and split it. +type CombinedId string + +// String return the identifier as a string +func (ci CombinedId) String() string { + return string(ci) +} + +// Human return the identifier, shortened for human consumption +func (ci CombinedId) Human() string { + format := fmt.Sprintf("%%.%ds", humanIdLength) + return fmt.Sprintf(format, ci) +} + +func (ci CombinedId) HasPrefix(prefix string) bool { + return strings.HasPrefix(string(ci), prefix) +} + +// UnmarshalGQL implement the Unmarshaler interface for gqlgen +func (ci *CombinedId) UnmarshalGQL(v interface{}) error { + _, ok := v.(string) + if !ok { + return fmt.Errorf("CombinedIds must be strings") + } + + *ci = v.(CombinedId) + + if err := ci.Validate(); err != nil { + return errors.Wrap(err, "invalid CombinedId") + } + + return nil +} + +// MarshalGQL implement the Marshaler interface for gqlgen +func (ci CombinedId) MarshalGQL(w io.Writer) { + _, _ = w.Write([]byte(`"` + ci.String() + `"`)) +} + +// Validate tell if the Id is valid +func (ci CombinedId) Validate() error { + // Special case to detect outdated repo + if len(ci) == 40 { + return fmt.Errorf("outdated repository format, please use https://github.com/MichaelMure/git-bug-migration to upgrade") + } + if len(ci) != idLength { + return fmt.Errorf("invalid length") + } + for _, r := range ci { + if (r < 'a' || r > 'z') && (r < '0' || r > '9') { + return fmt.Errorf("invalid character") + } + } + return nil +} + +// PrimaryPrefix is a helper to extract the primary prefix. +// If practical, use SeparateIds instead. +func (ci CombinedId) PrimaryPrefix() string { + primaryPrefix, _ := SeparateIds(string(ci)) + return primaryPrefix +} + +// SecondaryPrefix is a helper to extract the secondary prefix. +// If practical, use SeparateIds instead. +func (ci CombinedId) SecondaryPrefix() string { + _, secondaryPrefix := SeparateIds(string(ci)) + return secondaryPrefix +} + // CombineIds compute a merged Id holding information from both the primary Id // and the secondary Id. // -// This allow to later find efficiently a secondary element because we can access +// This allows to later find efficiently a secondary element because we can access // the primary one directly instead of searching for a primary that has a // secondary matching the Id. // @@ -32,7 +110,7 @@ import ( // 7: 4P, 3S // 10: 6P, 4S // 16: 11P, 5S -func CombineIds(primary Id, secondary Id) Id { +func CombineIds(primary Id, secondary Id) CombinedId { var id strings.Builder for i := 0; i < idLength; i++ { @@ -46,7 +124,7 @@ func CombineIds(primary Id, secondary Id) Id { } } - return Id(id.String()) + return CombinedId(id.String()) } // SeparateIds extract primary and secondary prefix from an arbitrary length prefix diff --git a/entity/id_interleaved_test.go b/entity/id_interleaved_test.go index ef9218c9..1dc79145 100644 --- a/entity/id_interleaved_test.go +++ b/entity/id_interleaved_test.go @@ -9,7 +9,7 @@ import ( func TestInterleaved(t *testing.T) { primary := Id("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWX______________") secondary := Id("YZ0123456789+/________________________________________________") - expectedId := Id("aYbZc0def1ghij2klmn3opqr4stuv5wxyz6ABCD7EFGH8IJKL9MNOP+QRST/UVWX") + expectedId := CombinedId("aYbZc0def1ghij2klmn3opqr4stuv5wxyz6ABCD7EFGH8IJKL9MNOP+QRST/UVWX") interleaved := CombineIds(primary, secondary) require.Equal(t, expectedId, interleaved) diff --git a/go.mod b/go.mod index 8ff7a40e..ffebd2a8 100644 --- a/go.mod +++ b/go.mod @@ -89,8 +89,10 @@ require ( github.com/steveyen/gtreap v0.1.0 // indirect github.com/stretchr/objx v0.4.0 // indirect github.com/tinylib/msgp v1.1.0 // indirect + github.com/urfave/cli/v2 v2.8.1 // indirect github.com/willf/bitset v1.1.10 // indirect github.com/xanzy/ssh-agent v0.3.0 // indirect + github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect go.etcd.io/bbolt v1.3.5 // indirect golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2 // indirect diff --git a/go.sum b/go.sum index 7289e676..b8853e7e 100644 --- a/go.sum +++ b/go.sum @@ -365,6 +365,7 @@ github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c/go.mod h1:ahpPrc7 github.com/tinylib/msgp v1.1.0 h1:9fQd+ICuRIu/ue4vxJZu6/LzxN0HwMds2nq/0cFvxHU= github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/urfave/cli/v2 v2.8.1 h1:CGuYNZF9IKZY/rfBe3lJpccSoIY1ytfvmgQT90cNOl4= github.com/urfave/cli/v2 v2.8.1/go.mod h1:Z41J9TPoffeoqP0Iza0YbAhGvymRdZAd2uPmZ5JxRdY= github.com/vektah/gqlparser/v2 v2.4.6 h1:Yjzp66g6oVq93Jihbi0qhGnf/6zIWjcm8H6gA27zstE= github.com/vektah/gqlparser/v2 v2.4.6/go.mod h1:flJWIR04IMQPGz+BXLrORkrARBxv/rtyIAFvd/MceW0= @@ -375,6 +376,7 @@ github.com/xanzy/go-gitlab v0.68.0/go.mod h1:o4yExCtdaqlM8YGdDJWuZoBmfxBsmA9TPEj github.com/xanzy/ssh-agent v0.3.0 h1:wUMzuKtKilRgBAD1sUb8gOwwRr2FGoBVumcjoOACClI= github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= diff --git a/repository/gogit.go b/repository/gogit.go index d94965cb..4e3ccfb8 100644 --- a/repository/gogit.go +++ b/repository/gogit.go @@ -476,6 +476,7 @@ func (repo *GoGitRepo) ReadData(hash Hash) ([]byte, error) { return nil, err } + // TODO: return a io.Reader instead return ioutil.ReadAll(r) } diff --git a/termui/show_bug.go b/termui/show_bug.go index 6cace04a..8bcae842 100644 --- a/termui/show_bug.go +++ b/termui/show_bug.go @@ -244,7 +244,7 @@ func (sb *showBug) renderMain(g *gocui.Gui, mainView *gocui.View) error { y0 += lines + 1 for _, op := range snap.Timeline { - viewName := op.Id().String() + viewName := op.CombinedId().String() // TODO: me might skip the rendering of blocks that are outside of the view // but to do that we need to rework how sb.mainSelectableView is maintained @@ -647,16 +647,16 @@ func (sb *showBug) edit(g *gocui.Gui, v *gocui.View) error { return nil } - op, err := snap.SearchTimelineItem(entity.Id(sb.selected)) + op, err := snap.SearchTimelineItem(entity.CombinedId(sb.selected)) if err != nil { return err } switch op := op.(type) { case *bug.AddCommentTimelineItem: - return editCommentWithEditor(sb.bug, op.Id(), op.Message) + return editCommentWithEditor(sb.bug, op.CombinedId(), op.Message) case *bug.CreateTimelineItem: - return editCommentWithEditor(sb.bug, op.Id(), op.Message) + return editCommentWithEditor(sb.bug, op.CombinedId(), op.Message) case *bug.LabelChangeTimelineItem: return sb.editLabels(g, snap) } diff --git a/termui/termui.go b/termui/termui.go index bbbc1c6c..15476d89 100644 --- a/termui/termui.go +++ b/termui/termui.go @@ -250,7 +250,7 @@ func addCommentWithEditor(bug *cache.BugCache) error { return errTerminateMainloop } -func editCommentWithEditor(bug *cache.BugCache, target entity.Id, preMessage string) error { +func editCommentWithEditor(bug *cache.BugCache, target entity.CombinedId, preMessage string) error { // This is somewhat hacky. // As there is no way to pause gocui, run the editor and restart gocui, // we have to stop it entirely and start a new one later. -- cgit