~edwargix/git.sr.ht

abe8b1501ac35582a3ca9f6fd40d7018fddc73e2 — Drew DeVault 5 years ago 5cd8a72
api: implement log, commit diff
M api/graph/generated/generated.go => api/graph/generated/generated.go +222 -23
@@ 38,6 38,7 @@ type Config struct {

type ResolverRoot interface {
	ACL() ACLResolver
	Commit() CommitResolver
	Mutation() MutationResolver
	Query() QueryResolver
	Repository() RepositoryResolver


@@ 83,6 84,7 @@ type ComplexityRoot struct {
	Commit struct {
		Author    func(childComplexity int) int
		Committer func(childComplexity int) int
		Diff      func(childComplexity int) int
		ID        func(childComplexity int) int
		Message   func(childComplexity int) int
		Parents   func(childComplexity int) int


@@ 92,6 94,11 @@ type ComplexityRoot struct {
		Type      func(childComplexity int) int
	}

	CommitCursor struct {
		Cursor  func(childComplexity int) int
		Results func(childComplexity int) int
	}

	Mutation struct {
		CreateRepository func(childComplexity int, params *model.RepoInput) int
		DeleteACL        func(childComplexity int, repoID int, entity string) int


@@ 129,7 136,7 @@ type ComplexityRoot struct {
		Description       func(childComplexity int) int
		Head              func(childComplexity int) int
		ID                func(childComplexity int) int
		Log               func(childComplexity int, cursor *model.Cursor) int
		Log               func(childComplexity int, cursor *model.Cursor, from *string) int
		Name              func(childComplexity int) int
		Objects           func(childComplexity int, ids []*string) int
		Owner             func(childComplexity int) int


@@ 212,6 219,9 @@ type ACLResolver interface {
	Repository(ctx context.Context, obj *model.ACL) (*model.Repository, error)
	Entity(ctx context.Context, obj *model.ACL) (model.Entity, error)
}
type CommitResolver interface {
	Diff(ctx context.Context, obj *model.Commit) (string, error)
}
type MutationResolver interface {
	CreateRepository(ctx context.Context, params *model.RepoInput) (*model.Repository, error)
	UpdateRepository(ctx context.Context, id string, params *model.RepoInput) (*model.Repository, error)


@@ 237,7 247,7 @@ type RepositoryResolver interface {
	References(ctx context.Context, obj *model.Repository, cursor *model.Cursor) (*model.ReferenceCursor, error)
	Objects(ctx context.Context, obj *model.Repository, ids []*string) ([]model.Object, error)

	Log(ctx context.Context, obj *model.Repository, cursor *model.Cursor) ([]*model.Commit, error)
	Log(ctx context.Context, obj *model.Repository, cursor *model.Cursor, from *string) (*model.CommitCursor, error)
	Path(ctx context.Context, obj *model.Repository, revspec *string, path string) (*model.TreeEntry, error)
	RevparseSingle(ctx context.Context, obj *model.Repository, revspec string) (*model.Commit, error)
}


@@ 410,6 420,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in

		return e.complexity.Commit.Committer(childComplexity), true

	case "Commit.diff":
		if e.complexity.Commit.Diff == nil {
			break
		}

		return e.complexity.Commit.Diff(childComplexity), true

	case "Commit.id":
		if e.complexity.Commit.ID == nil {
			break


@@ 459,6 476,20 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in

		return e.complexity.Commit.Type(childComplexity), true

	case "CommitCursor.cursor":
		if e.complexity.CommitCursor.Cursor == nil {
			break
		}

		return e.complexity.CommitCursor.Cursor(childComplexity), true

	case "CommitCursor.results":
		if e.complexity.CommitCursor.Results == nil {
			break
		}

		return e.complexity.CommitCursor.Results(childComplexity), true

	case "Mutation.createRepository":
		if e.complexity.Mutation.CreateRepository == nil {
			break


@@ 702,7 733,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
			return 0, false
		}

		return e.complexity.Repository.Log(childComplexity, args["cursor"].(*model.Cursor)), true
		return e.complexity.Repository.Log(childComplexity, args["cursor"].(*model.Cursor), args["from"].(*string)), true

	case "Repository.name":
		if e.complexity.Repository.Name == nil {


@@ 1257,9 1288,11 @@ type Repository {
  # The HEAD reference for this repository (equivalent to the default branch)
  HEAD: Reference

  # Returns a list of comments in topological order. ` + "`" + `cursor.from` + "`" + ` is used as
  # the revspec to begin logging from.
  log(cursor: Cursor): [Commit]!
  # Returns a list of comments in topological order.
  #
  # If ` + "`" + `from` + "`" + ` is specified, it is interpreted as a revspec to start logging
  # from.
  log(cursor: Cursor, from: String): CommitCursor

  #
  # Returns a tree entry for a given path and revspec


@@ 1301,6 1334,16 @@ type ReferenceCursor {
  cursor: Cursor
}

# A cursor for enumerating commits
#
# If there are additional results available, the cursor object may be passed
# back into the same endpoint to retrieve another page. If the cursor is null,
# there are no remaining results to return.
type CommitCursor {
  results: [Commit]!
  cursor: Cursor
}

# Access Control List entry
type ACL {
  id: Int!


@@ 1358,6 1401,7 @@ type Commit implements Object {
  message: String!
  tree: Tree!
  parents: [Commit!]!
  diff: String!
}

type Tree implements Object {


@@ 1764,6 1808,14 @@ func (ec *executionContext) field_Repository_log_args(ctx context.Context, rawAr
		}
	}
	args["cursor"] = arg0
	var arg1 *string
	if tmp, ok := rawArgs["from"]; ok {
		arg1, err = ec.unmarshalOString2ᚖstring(ctx, tmp)
		if err != nil {
			return nil, err
		}
	}
	args["from"] = arg1
	return args, nil
}



@@ 2863,6 2915,105 @@ func (ec *executionContext) _Commit_parents(ctx context.Context, field graphql.C
	return ec.marshalNCommit2ᚕᚖgitᚗsrᚗhtᚋאsircmpwnᚋgitᚗsrᚗhtᚋapiᚋgraphᚋmodelᚐCommitᚄ(ctx, field.Selections, res)
}

func (ec *executionContext) _Commit_diff(ctx context.Context, field graphql.CollectedField, obj *model.Commit) (ret graphql.Marshaler) {
	defer func() {
		if r := recover(); r != nil {
			ec.Error(ctx, ec.Recover(ctx, r))
			ret = graphql.Null
		}
	}()
	fc := &graphql.FieldContext{
		Object:   "Commit",
		Field:    field,
		Args:     nil,
		IsMethod: true,
	}

	ctx = graphql.WithFieldContext(ctx, fc)
	resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
		ctx = rctx // use context from middleware stack in children
		return ec.resolvers.Commit().Diff(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.(string)
	fc.Result = res
	return ec.marshalNString2string(ctx, field.Selections, res)
}

func (ec *executionContext) _CommitCursor_results(ctx context.Context, field graphql.CollectedField, obj *model.CommitCursor) (ret graphql.Marshaler) {
	defer func() {
		if r := recover(); r != nil {
			ec.Error(ctx, ec.Recover(ctx, r))
			ret = graphql.Null
		}
	}()
	fc := &graphql.FieldContext{
		Object:   "CommitCursor",
		Field:    field,
		Args:     nil,
		IsMethod: false,
	}

	ctx = graphql.WithFieldContext(ctx, fc)
	resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
		ctx = rctx // use context from middleware stack in children
		return obj.Results, nil
	})
	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.([]*model.Commit)
	fc.Result = res
	return ec.marshalNCommit2ᚕᚖgitᚗsrᚗhtᚋאsircmpwnᚋgitᚗsrᚗhtᚋapiᚋgraphᚋmodelᚐCommit(ctx, field.Selections, res)
}

func (ec *executionContext) _CommitCursor_cursor(ctx context.Context, field graphql.CollectedField, obj *model.CommitCursor) (ret graphql.Marshaler) {
	defer func() {
		if r := recover(); r != nil {
			ec.Error(ctx, ec.Recover(ctx, r))
			ret = graphql.Null
		}
	}()
	fc := &graphql.FieldContext{
		Object:   "CommitCursor",
		Field:    field,
		Args:     nil,
		IsMethod: false,
	}

	ctx = graphql.WithFieldContext(ctx, fc)
	resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
		ctx = rctx // use context from middleware stack in children
		return obj.Cursor, nil
	})
	if err != nil {
		ec.Error(ctx, err)
		return graphql.Null
	}
	if resTmp == nil {
		return graphql.Null
	}
	res := resTmp.(*model.Cursor)
	fc.Result = res
	return ec.marshalOCursor2ᚖgitᚗsrᚗhtᚋאsircmpwnᚋgitᚗsrᚗhtᚋapiᚋgraphᚋmodelᚐCursor(ctx, field.Selections, res)
}

func (ec *executionContext) _Mutation_createRepository(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
	defer func() {
		if r := recover(); r != nil {


@@ 4079,21 4230,18 @@ func (ec *executionContext) _Repository_log(ctx context.Context, field graphql.C
	fc.Args = args
	resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
		ctx = rctx // use context from middleware stack in children
		return ec.resolvers.Repository().Log(rctx, obj, args["cursor"].(*model.Cursor))
		return ec.resolvers.Repository().Log(rctx, obj, args["cursor"].(*model.Cursor), args["from"].(*string))
	})
	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.([]*model.Commit)
	res := resTmp.(*model.CommitCursor)
	fc.Result = res
	return ec.marshalNCommit2ᚕᚖgitᚗsrᚗhtᚋאsircmpwnᚋgitᚗsrᚗhtᚋapiᚋgraphᚋmodelᚐCommit(ctx, field.Selections, res)
	return ec.marshalOCommitCursor2ᚖgitᚗsrᚗhtᚋאsircmpwnᚋgitᚗsrᚗhtᚋapiᚋgraphᚋmodelᚐCommitCursor(ctx, field.Selections, res)
}

func (ec *executionContext) _Repository_path(ctx context.Context, field graphql.CollectedField, obj *model.Repository) (ret graphql.Marshaler) {


@@ 7016,48 7164,91 @@ func (ec *executionContext) _Commit(ctx context.Context, sel ast.SelectionSet, o
		case "type":
			out.Values[i] = ec._Commit_type(ctx, field, obj)
			if out.Values[i] == graphql.Null {
				invalids++
				atomic.AddUint32(&invalids, 1)
			}
		case "id":
			out.Values[i] = ec._Commit_id(ctx, field, obj)
			if out.Values[i] == graphql.Null {
				invalids++
				atomic.AddUint32(&invalids, 1)
			}
		case "shortId":
			out.Values[i] = ec._Commit_shortId(ctx, field, obj)
			if out.Values[i] == graphql.Null {
				invalids++
				atomic.AddUint32(&invalids, 1)
			}
		case "raw":
			out.Values[i] = ec._Commit_raw(ctx, field, obj)
			if out.Values[i] == graphql.Null {
				invalids++
				atomic.AddUint32(&invalids, 1)
			}
		case "author":
			out.Values[i] = ec._Commit_author(ctx, field, obj)
			if out.Values[i] == graphql.Null {
				invalids++
				atomic.AddUint32(&invalids, 1)
			}
		case "committer":
			out.Values[i] = ec._Commit_committer(ctx, field, obj)
			if out.Values[i] == graphql.Null {
				invalids++
				atomic.AddUint32(&invalids, 1)
			}
		case "message":
			out.Values[i] = ec._Commit_message(ctx, field, obj)
			if out.Values[i] == graphql.Null {
				invalids++
				atomic.AddUint32(&invalids, 1)
			}
		case "tree":
			out.Values[i] = ec._Commit_tree(ctx, field, obj)
			if out.Values[i] == graphql.Null {
				invalids++
				atomic.AddUint32(&invalids, 1)
			}
		case "parents":
			out.Values[i] = ec._Commit_parents(ctx, field, obj)
			if out.Values[i] == graphql.Null {
				atomic.AddUint32(&invalids, 1)
			}
		case "diff":
			field := field
			out.Concurrently(i, func() (res graphql.Marshaler) {
				defer func() {
					if r := recover(); r != nil {
						ec.Error(ctx, ec.Recover(ctx, r))
					}
				}()
				res = ec._Commit_diff(ctx, field, obj)
				if res == graphql.Null {
					atomic.AddUint32(&invalids, 1)
				}
				return res
			})
		default:
			panic("unknown field " + strconv.Quote(field.Name))
		}
	}
	out.Dispatch()
	if invalids > 0 {
		return graphql.Null
	}
	return out
}

var commitCursorImplementors = []string{"CommitCursor"}

func (ec *executionContext) _CommitCursor(ctx context.Context, sel ast.SelectionSet, obj *model.CommitCursor) graphql.Marshaler {
	fields := graphql.CollectFields(ec.OperationContext, sel, commitCursorImplementors)

	out := graphql.NewFieldSet(fields)
	var invalids uint32
	for i, field := range fields {
		switch field.Name {
		case "__typename":
			out.Values[i] = graphql.MarshalString("CommitCursor")
		case "results":
			out.Values[i] = ec._CommitCursor_results(ctx, field, obj)
			if out.Values[i] == graphql.Null {
				invalids++
			}
		case "cursor":
			out.Values[i] = ec._CommitCursor_cursor(ctx, field, obj)
		default:
			panic("unknown field " + strconv.Quote(field.Name))
		}


@@ 7407,9 7598,6 @@ func (ec *executionContext) _Repository(ctx context.Context, sel ast.SelectionSe
					}
				}()
				res = ec._Repository_log(ctx, field, obj)
				if res == graphql.Null {
					atomic.AddUint32(&invalids, 1)
				}
				return res
			})
		case "path":


@@ 8892,6 9080,17 @@ func (ec *executionContext) marshalOCommit2ᚖgitᚗsrᚗhtᚋאsircmpwnᚋgit
	return ec._Commit(ctx, sel, v)
}

func (ec *executionContext) marshalOCommitCursor2gitᚗsrᚗhtᚋאsircmpwnᚋgitᚗsrᚗhtᚋapiᚋgraphᚋmodelᚐCommitCursor(ctx context.Context, sel ast.SelectionSet, v model.CommitCursor) graphql.Marshaler {
	return ec._CommitCursor(ctx, sel, &v)
}

func (ec *executionContext) marshalOCommitCursor2ᚖgitᚗsrᚗhtᚋאsircmpwnᚋgitᚗsrᚗhtᚋapiᚋgraphᚋmodelᚐCommitCursor(ctx context.Context, sel ast.SelectionSet, v *model.CommitCursor) graphql.Marshaler {
	if v == nil {
		return graphql.Null
	}
	return ec._CommitCursor(ctx, sel, v)
}

func (ec *executionContext) unmarshalOCursor2gitᚗsrᚗhtᚋאsircmpwnᚋgitᚗsrᚗhtᚋapiᚋgraphᚋmodelᚐCursor(ctx context.Context, v interface{}) (model.Cursor, error) {
	var res model.Cursor
	return res, res.UnmarshalGQL(v)

M api/graph/model/commit.go => api/graph/model/commit.go +22 -0
@@ 1,6 1,8 @@
package model

import (
	"context"

	"github.com/go-git/go-git/v5"
	"github.com/go-git/go-git/v5/plumbing/object"
)


@@ 37,6 39,15 @@ func (c *Commit) Committer() *Signature {
	}
}

func (c *Commit) DiffContext(ctx context.Context) string {
	parent, _ := c.commit.Parent(0)
	patch, _ := c.commit.PatchContext(ctx, parent)
	if patch != nil {
		return patch.String()
	}
	return ""
}

func (c *Commit) Tree() *Tree {
	obj, err := LookupObject(c.repo, c.commit.TreeHash)
	if err != nil {


@@ 58,3 69,14 @@ func (c *Commit) Parents() []*Commit {
	}
	return parents
}

func CommitFromObject(repo *git.Repository, obj *object.Commit) *Commit {
	return &Commit{
		Type:    ObjectTypeCommit,
		ID:      obj.ID().String(),
		ShortID: obj.ID().String()[:7],

		commit: obj,
		repo:   repo,
	}
}

M api/graph/model/models_gen.go => api/graph/model/models_gen.go +5 -0
@@ 32,6 32,11 @@ type Artifact struct {
	URL        string      `json:"url"`
}

type CommitCursor struct {
	Results []*Commit `json:"results"`
	Cursor  *Cursor   `json:"cursor"`
}

type Filter struct {
	Count   *int    `json:"count"`
	Search  *string `json:"search"`

M api/graph/model/object.go => api/graph/model/object.go +1 -8
@@ 21,14 21,7 @@ func LookupObject(repo *git.Repository, hash plumbing.Hash) (Object, error) {
	// TODO: Add raw object data, if requested
	switch obj := obj.(type) {
	case *object.Commit:
		return &Commit{
			Type:    ObjectTypeCommit,
			ID:      obj.ID().String(),
			ShortID: obj.ID().String()[:7],

			commit: obj,
			repo:   repo,
		}, nil
		return CommitFromObject(repo, obj), nil
	case *object.Tree:
		return TreeFromObject(repo, obj), nil
	case *object.Blob:

M api/graph/schema.graphqls => api/graph/schema.graphqls +16 -3
@@ 95,9 95,11 @@ type Repository {
  # The HEAD reference for this repository (equivalent to the default branch)
  HEAD: Reference

  # Returns a list of comments in topological order. `cursor.from` is used as
  # the revspec to begin logging from.
  log(cursor: Cursor): [Commit]!
  # Returns a list of comments in topological order.
  #
  # If `from` is specified, it is interpreted as a revspec to start logging
  # from.
  log(cursor: Cursor, from: String): CommitCursor

  #
  # Returns a tree entry for a given path and revspec


@@ 139,6 141,16 @@ type ReferenceCursor {
  cursor: Cursor
}

# A cursor for enumerating commits
#
# If there are additional results available, the cursor object may be passed
# back into the same endpoint to retrieve another page. If the cursor is null,
# there are no remaining results to return.
type CommitCursor {
  results: [Commit]!
  cursor: Cursor
}

# Access Control List entry
type ACL {
  id: Int!


@@ 196,6 208,7 @@ type Commit implements Object {
  message: String!
  tree: Tree!
  parents: [Commit!]!
  diff: String!
}

type Tree implements Object {

M api/graph/schema.resolvers.go => api/graph/schema.resolvers.go +58 -2
@@ 15,8 15,10 @@ import (
	"git.sr.ht/~sircmpwn/git.sr.ht/api/graph/model"
	"git.sr.ht/~sircmpwn/git.sr.ht/api/loaders"
	"git.sr.ht/~sircmpwn/gqlgen/graphql"
	git "github.com/go-git/go-git/v5"
	"github.com/go-git/go-git/v5/plumbing"
	"github.com/go-git/go-git/v5/plumbing/object"
	"github.com/go-git/go-git/v5/plumbing/storer"
)

func (r *aCLResolver) Repository(ctx context.Context, obj *model.ACL) (*model.Repository, error) {


@@ 53,6 55,10 @@ func (r *aCLResolver) Entity(ctx context.Context, obj *model.ACL) (model.Entity,
	return user, nil
}

func (r *commitResolver) Diff(ctx context.Context, obj *model.Commit) (string, error) {
	return obj.DiffContext(ctx), nil
}

func (r *mutationResolver) CreateRepository(ctx context.Context, params *model.RepoInput) (*model.Repository, error) {
	panic(fmt.Errorf("createRepository: not implemented"))
}


@@ 212,8 218,54 @@ func (r *repositoryResolver) Objects(ctx context.Context, obj *model.Repository,
	panic(fmt.Errorf("not implemented"))
}

func (r *repositoryResolver) Log(ctx context.Context, obj *model.Repository, cursor *model.Cursor) ([]*model.Commit, error) {
	panic(fmt.Errorf("not implemented"))
func (r *repositoryResolver) Log(ctx context.Context, obj *model.Repository, cursor *model.Cursor, from *string) (*model.CommitCursor, error) {
	if cursor == nil {
		cursor = model.NewCursor(nil)
		if from != nil {
			cursor.Next = *from
		}
	}

	opts := &git.LogOptions{
		Order: git.LogOrderCommitterTime,
	}
	if cursor.Next != "" {
		rev, err := obj.Repo().ResolveRevision(plumbing.Revision(cursor.Next))
		if err != nil {
			return nil, err
		}
		if rev == nil {
			return nil, fmt.Errorf("No such revision")
		}
		opts.From = *rev
	}

	log, err := obj.Repo().Log(opts)
	if err != nil {
		return nil, err
	}

	var commits []*model.Commit
	log.ForEach(func(c *object.Commit) error {
		commits = append(commits, model.CommitFromObject(obj.Repo(), c))
		if len(commits) == cursor.Count+1 {
			return storer.ErrStop
		}
		return nil
	})

	if len(commits) > cursor.Count {
		cursor = &model.Cursor{
			Count:  cursor.Count,
			Next:   commits[cursor.Count].ID,
			Search: "",
		}
		commits = commits[:cursor.Count]
	} else {
		cursor = nil
	}

	return &model.CommitCursor{commits, cursor}, nil
}

func (r *repositoryResolver) Path(ctx context.Context, obj *model.Repository, revspec *string, path string) (*model.TreeEntry, error) {


@@ 296,6 348,9 @@ func (r *userResolver) Repositories(ctx context.Context, obj *model.User, cursor
// ACL returns generated.ACLResolver implementation.
func (r *Resolver) ACL() generated.ACLResolver { return &aCLResolver{r} }

// Commit returns generated.CommitResolver implementation.
func (r *Resolver) Commit() generated.CommitResolver { return &commitResolver{r} }

// Mutation returns generated.MutationResolver implementation.
func (r *Resolver) Mutation() generated.MutationResolver { return &mutationResolver{r} }



@@ 312,6 367,7 @@ func (r *Resolver) Tree() generated.TreeResolver { return &treeResolver{r} }
func (r *Resolver) User() generated.UserResolver { return &userResolver{r} }

type aCLResolver struct{ *Resolver }
type commitResolver struct{ *Resolver }
type mutationResolver struct{ *Resolver }
type queryResolver struct{ *Resolver }
type repositoryResolver struct{ *Resolver }