~edwargix/git.sr.ht

afa86ab568b07a48244272d7b14b3ceba7b8d4db — Drew DeVault 5 years ago 5277353
api: implement tree objects
M graphql/graph/generated/generated.go => graphql/graph/generated/generated.go +358 -14
@@ 65,6 65,10 @@ type ComplexityRoot struct {
		URL        func(childComplexity int) int
	}

	BinaryBlob struct {
		Base64 func(childComplexity int) int
	}

	Blob struct {
		BlobType func(childComplexity int) int
		Data     func(childComplexity int) int


@@ 148,8 152,13 @@ type ComplexityRoot struct {
		Type    func(childComplexity int) int
	}

	TextBlob struct {
		Text func(childComplexity int) int
	}

	Tree struct {
		Entries func(childComplexity int) int
		Entries func(childComplexity int, count *int, next *string) int
		Entry   func(childComplexity int, path string) int
		ID      func(childComplexity int) int
		Raw     func(childComplexity int) int
		ShortID func(childComplexity int) int


@@ 157,6 166,7 @@ type ComplexityRoot struct {
	}

	TreeEntry struct {
		ID     func(childComplexity int) int
		Mode   func(childComplexity int) int
		Name   func(childComplexity int) int
		Object func(childComplexity int) int


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

		return e.complexity.Artifact.URL(childComplexity), true

	case "BinaryBlob.base64":
		if e.complexity.BinaryBlob.Base64 == nil {
			break
		}

		return e.complexity.BinaryBlob.Base64(childComplexity), true

	case "Blob.blobType":
		if e.complexity.Blob.BlobType == nil {
			break


@@ 817,12 834,36 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in

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

	case "TextBlob.text":
		if e.complexity.TextBlob.Text == nil {
			break
		}

		return e.complexity.TextBlob.Text(childComplexity), true

	case "Tree.entries":
		if e.complexity.Tree.Entries == nil {
			break
		}

		return e.complexity.Tree.Entries(childComplexity), true
		args, err := ec.field_Tree_entries_args(context.TODO(), rawArgs)
		if err != nil {
			return 0, false
		}

		return e.complexity.Tree.Entries(childComplexity, args["count"].(*int), args["next"].(*string)), true

	case "Tree.entry":
		if e.complexity.Tree.Entry == nil {
			break
		}

		args, err := ec.field_Tree_entry_args(context.TODO(), rawArgs)
		if err != nil {
			return 0, false
		}

		return e.complexity.Tree.Entry(childComplexity, args["path"].(string)), true

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


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

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

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

		return e.complexity.TreeEntry.ID(childComplexity), true

	case "TreeEntry.mode":
		if e.complexity.TreeEntry.Mode == nil {
			break


@@ 1219,10 1267,13 @@ type Tree implements Object {
  id: String!
  shortId: String!
  raw: String!
  entries: [TreeEntry!]!
  entries(count: Int = 100, next: String): [TreeEntry!]!

  entry(path: String): TreeEntry
}

type TreeEntry {
  id: String!
  name: String!
  object: Object!
  # Unix-style file mode, i.e. 0755 or 0644 (octal)


@@ 1234,15 1285,23 @@ enum BlobType {
  TEXT
}

type BinaryBlob {
  base64: String!
}

type TextBlob {
  text: String!
}

union BlobData = BinaryBlob | TextBlob

type Blob implements Object {
  type: ObjectType!
  id: String!
  shortId: String!
  raw: String!
  blobType: BlobType!
  # If blobType is BINARY, this will be base64 encoded; otherwise it will be
  # the blob's text contents verbatim
  data: String
  data: BlobData
}

type Tag implements Object {


@@ 1720,6 1779,42 @@ func (ec *executionContext) field_Repository_tree_args(ctx context.Context, rawA
	return args, nil
}

func (ec *executionContext) field_Tree_entries_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
	var err error
	args := map[string]interface{}{}
	var arg0 *int
	if tmp, ok := rawArgs["count"]; ok {
		arg0, err = ec.unmarshalOInt2ᚖint(ctx, tmp)
		if err != nil {
			return nil, err
		}
	}
	args["count"] = arg0
	var arg1 *string
	if tmp, ok := rawArgs["next"]; ok {
		arg1, err = ec.unmarshalOString2ᚖstring(ctx, tmp)
		if err != nil {
			return nil, err
		}
	}
	args["next"] = arg1
	return args, nil
}

func (ec *executionContext) field_Tree_entry_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
	var err error
	args := map[string]interface{}{}
	var arg0 string
	if tmp, ok := rawArgs["path"]; ok {
		arg0, err = ec.unmarshalOString2string(ctx, tmp)
		if err != nil {
			return nil, err
		}
	}
	args["path"] = arg0
	return args, nil
}

func (ec *executionContext) field_User_repositories_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
	var err error
	args := map[string]interface{}{}


@@ 2191,6 2286,40 @@ func (ec *executionContext) _Artifact_url(ctx context.Context, field graphql.Col
	return ec.marshalNString2string(ctx, field.Selections, res)
}

func (ec *executionContext) _BinaryBlob_base64(ctx context.Context, field graphql.CollectedField, obj *model.BinaryBlob) (ret graphql.Marshaler) {
	defer func() {
		if r := recover(); r != nil {
			ec.Error(ctx, ec.Recover(ctx, r))
			ret = graphql.Null
		}
	}()
	fc := &graphql.FieldContext{
		Object:   "BinaryBlob",
		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.Base64, 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.(string)
	fc.Result = res
	return ec.marshalNString2string(ctx, field.Selections, res)
}

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


@@ 2387,9 2516,9 @@ func (ec *executionContext) _Blob_data(ctx context.Context, field graphql.Collec
	if resTmp == nil {
		return graphql.Null
	}
	res := resTmp.(*string)
	res := resTmp.(model.BlobData)
	fc.Result = res
	return ec.marshalOString2ᚖstring(ctx, field.Selections, res)
	return ec.marshalOBlobData2gitᚗsrᚗhtᚋאsircmpwnᚋgitᚗsrᚗhtᚋgraphqlᚋgraphᚋmodelᚐBlobData(ctx, field.Selections, res)
}

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


@@ 2641,13 2770,13 @@ func (ec *executionContext) _Commit_tree(ctx context.Context, field graphql.Coll
		Object:   "Commit",
		Field:    field,
		Args:     nil,
		IsMethod: false,
		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 obj.Tree, nil
		return obj.Tree(), nil
	})
	if err != nil {
		ec.Error(ctx, err)


@@ 4360,6 4489,40 @@ func (ec *executionContext) _Tag_message(ctx context.Context, field graphql.Coll
	return ec.marshalOString2ᚖstring(ctx, field.Selections, res)
}

func (ec *executionContext) _TextBlob_text(ctx context.Context, field graphql.CollectedField, obj *model.TextBlob) (ret graphql.Marshaler) {
	defer func() {
		if r := recover(); r != nil {
			ec.Error(ctx, ec.Recover(ctx, r))
			ret = graphql.Null
		}
	}()
	fc := &graphql.FieldContext{
		Object:   "TextBlob",
		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.Text, 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.(string)
	fc.Result = res
	return ec.marshalNString2string(ctx, field.Selections, res)
}

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


@@ 4507,13 4670,20 @@ func (ec *executionContext) _Tree_entries(ctx context.Context, field graphql.Col
		Object:   "Tree",
		Field:    field,
		Args:     nil,
		IsMethod: false,
		IsMethod: true,
	}

	ctx = graphql.WithFieldContext(ctx, fc)
	rawArgs := field.ArgumentMap(ec.Variables)
	args, err := ec.field_Tree_entries_args(ctx, rawArgs)
	if err != nil {
		ec.Error(ctx, err)
		return graphql.Null
	}
	fc.Args = args
	resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
		ctx = rctx // use context from middleware stack in children
		return obj.Entries, nil
		return obj.Entries(args["count"].(*int), args["next"].(*string)), nil
	})
	if err != nil {
		ec.Error(ctx, err)


@@ 4530,6 4700,78 @@ func (ec *executionContext) _Tree_entries(ctx context.Context, field graphql.Col
	return ec.marshalNTreeEntry2ᚕᚖgitᚗsrᚗhtᚋאsircmpwnᚋgitᚗsrᚗhtᚋgraphqlᚋgraphᚋmodelᚐTreeEntryᚄ(ctx, field.Selections, res)
}

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

	ctx = graphql.WithFieldContext(ctx, fc)
	rawArgs := field.ArgumentMap(ec.Variables)
	args, err := ec.field_Tree_entry_args(ctx, rawArgs)
	if err != nil {
		ec.Error(ctx, err)
		return graphql.Null
	}
	fc.Args = args
	resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
		ctx = rctx // use context from middleware stack in children
		return obj.Entry(args["path"].(string)), nil
	})
	if err != nil {
		ec.Error(ctx, err)
		return graphql.Null
	}
	if resTmp == nil {
		return graphql.Null
	}
	res := resTmp.(*model.TreeEntry)
	fc.Result = res
	return ec.marshalOTreeEntry2ᚖgitᚗsrᚗhtᚋאsircmpwnᚋgitᚗsrᚗhtᚋgraphqlᚋgraphᚋmodelᚐTreeEntry(ctx, field.Selections, res)
}

func (ec *executionContext) _TreeEntry_id(ctx context.Context, field graphql.CollectedField, obj *model.TreeEntry) (ret graphql.Marshaler) {
	defer func() {
		if r := recover(); r != nil {
			ec.Error(ctx, ec.Recover(ctx, r))
			ret = graphql.Null
		}
	}()
	fc := &graphql.FieldContext{
		Object:   "TreeEntry",
		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 obj.ID(), 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.(string)
	fc.Result = res
	return ec.marshalNString2string(ctx, field.Selections, res)
}

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


@@ 4575,13 4817,13 @@ func (ec *executionContext) _TreeEntry_object(ctx context.Context, field graphql
		Object:   "TreeEntry",
		Field:    field,
		Args:     nil,
		IsMethod: false,
		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 obj.Object, nil
		return obj.Object(), nil
	})
	if err != nil {
		ec.Error(ctx, err)


@@ 6210,6 6452,29 @@ func (ec *executionContext) unmarshalInputRepoInput(ctx context.Context, obj int

// region    ************************** interface.gotpl ***************************

func (ec *executionContext) _BlobData(ctx context.Context, sel ast.SelectionSet, obj model.BlobData) graphql.Marshaler {
	switch obj := (obj).(type) {
	case nil:
		return graphql.Null
	case model.BinaryBlob:
		return ec._BinaryBlob(ctx, sel, &obj)
	case *model.BinaryBlob:
		if obj == nil {
			return graphql.Null
		}
		return ec._BinaryBlob(ctx, sel, obj)
	case model.TextBlob:
		return ec._TextBlob(ctx, sel, &obj)
	case *model.TextBlob:
		if obj == nil {
			return graphql.Null
		}
		return ec._TextBlob(ctx, sel, obj)
	default:
		panic(fmt.Errorf("unexpected type %T", obj))
	}
}

func (ec *executionContext) _Entity(ctx context.Context, sel ast.SelectionSet, obj model.Entity) graphql.Marshaler {
	switch obj := (obj).(type) {
	case nil:


@@ 6368,6 6633,33 @@ func (ec *executionContext) _Artifact(ctx context.Context, sel ast.SelectionSet,
	return out
}

var binaryBlobImplementors = []string{"BinaryBlob", "BlobData"}

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

	out := graphql.NewFieldSet(fields)
	var invalids uint32
	for i, field := range fields {
		switch field.Name {
		case "__typename":
			out.Values[i] = graphql.MarshalString("BinaryBlob")
		case "base64":
			out.Values[i] = ec._BinaryBlob_base64(ctx, field, obj)
			if out.Values[i] == graphql.Null {
				invalids++
			}
		default:
			panic("unknown field " + strconv.Quote(field.Name))
		}
	}
	out.Dispatch()
	if invalids > 0 {
		return graphql.Null
	}
	return out
}

var blobImplementors = []string{"Blob", "Object"}

func (ec *executionContext) _Blob(ctx context.Context, sel ast.SelectionSet, obj *model.Blob) graphql.Marshaler {


@@ 6893,6 7185,33 @@ func (ec *executionContext) _Tag(ctx context.Context, sel ast.SelectionSet, obj 
	return out
}

var textBlobImplementors = []string{"TextBlob", "BlobData"}

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

	out := graphql.NewFieldSet(fields)
	var invalids uint32
	for i, field := range fields {
		switch field.Name {
		case "__typename":
			out.Values[i] = graphql.MarshalString("TextBlob")
		case "text":
			out.Values[i] = ec._TextBlob_text(ctx, field, obj)
			if out.Values[i] == graphql.Null {
				invalids++
			}
		default:
			panic("unknown field " + strconv.Quote(field.Name))
		}
	}
	out.Dispatch()
	if invalids > 0 {
		return graphql.Null
	}
	return out
}

var treeImplementors = []string{"Tree", "Object"}

func (ec *executionContext) _Tree(ctx context.Context, sel ast.SelectionSet, obj *model.Tree) graphql.Marshaler {


@@ 6929,6 7248,8 @@ func (ec *executionContext) _Tree(ctx context.Context, sel ast.SelectionSet, obj
			if out.Values[i] == graphql.Null {
				invalids++
			}
		case "entry":
			out.Values[i] = ec._Tree_entry(ctx, field, obj)
		default:
			panic("unknown field " + strconv.Quote(field.Name))
		}


@@ 6951,6 7272,11 @@ func (ec *executionContext) _TreeEntry(ctx context.Context, sel ast.SelectionSet
		switch field.Name {
		case "__typename":
			out.Values[i] = graphql.MarshalString("TreeEntry")
		case "id":
			out.Values[i] = ec._TreeEntry_id(ctx, field, obj)
			if out.Values[i] == graphql.Null {
				invalids++
			}
		case "name":
			out.Values[i] = ec._TreeEntry_name(ctx, field, obj)
			if out.Values[i] == graphql.Null {


@@ 8130,6 8456,13 @@ func (ec *executionContext) marshalOBlob2ᚖgitᚗsrᚗhtᚋאsircmpwnᚋgitᚗs
	return ec._Blob(ctx, sel, v)
}

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

func (ec *executionContext) unmarshalOBoolean2bool(ctx context.Context, v interface{}) (bool, error) {
	return graphql.UnmarshalBoolean(v)
}


@@ 8329,6 8662,17 @@ func (ec *executionContext) marshalOTree2ᚖgitᚗsrᚗhtᚋאsircmpwnᚋgitᚗs
	return ec._Tree(ctx, sel, v)
}

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

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

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

A graphql/graph/model/commit.go => graphql/graph/model/commit.go +48 -0
@@ 0,0 1,48 @@
package model

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

type Commit struct {
	Type      ObjectType `json:"type"`
	ID        string     `json:"id"`
	ShortID   string     `json:"shortId"`
	Raw       string     `json:"raw"`
	Parents   []*Commit  `json:"parents"`

	commit *object.Commit
	repo   *git.Repository
}

func (Commit) IsObject() {}

func (c *Commit) Message() string {
	return c.commit.Message
}

func (c *Commit) Author() *Signature {
	return &Signature{
		Name: c.commit.Author.Name,
		Email: c.commit.Author.Email,
		Time: c.commit.Author.When,
	}
}

func (c *Commit) Committer() *Signature {
	return &Signature{
		Name: c.commit.Committer.Name,
		Email: c.commit.Committer.Email,
		Time: c.commit.Committer.When,
	}
}

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

M graphql/graph/model/models_gen.go => graphql/graph/model/models_gen.go +14 -14
@@ 9,6 9,10 @@ import (
	"time"
)

type BlobData interface {
	IsBlobData()
}

type Entity interface {
	IsEntity()
}


@@ 31,13 35,19 @@ type Artifact struct {
	URL        string      `json:"url"`
}

type BinaryBlob struct {
	Base64 string `json:"base64"`
}

func (BinaryBlob) IsBlobData() {}

type Blob struct {
	Type     ObjectType `json:"type"`
	ID       string     `json:"id"`
	ShortID  string     `json:"shortId"`
	Raw      string     `json:"raw"`
	BlobType BlobType   `json:"blobType"`
	Data     *string    `json:"data"`
	Data     BlobData   `json:"data"`
}

func (Blob) IsObject() {}


@@ 71,21 81,11 @@ type Tag struct {

func (Tag) IsObject() {}

type Tree struct {
	Type    ObjectType   `json:"type"`
	ID      string       `json:"id"`
	ShortID string       `json:"shortId"`
	Raw     string       `json:"raw"`
	Entries []*TreeEntry `json:"entries"`
type TextBlob struct {
	Text string `json:"text"`
}

func (Tree) IsObject() {}

type TreeEntry struct {
	Name   string `json:"name"`
	Object Object `json:"object"`
	Mode   int    `json:"mode"`
}
func (TextBlob) IsBlobData() {}

type Version struct {
	Major           int        `json:"major"`

M graphql/graph/model/object.go => graphql/graph/model/object.go +12 -33
@@ 18,6 18,7 @@ func LookupObject(repo *git.Repository, hash plumbing.Hash) (Object, error) {
	if err != nil {
		return nil, fmt.Errorf("lookup object %s: %w", hash.String(), err)
	}
	// TODO: Add raw object data, if requested
	switch obj := obj.(type) {
	case *object.Commit:
		return &Commit{


@@ 25,42 26,20 @@ func LookupObject(repo *git.Repository, hash plumbing.Hash) (Object, error) {
			ID:      obj.ID().String(),
			ShortID: obj.ID().String()[:7],

			commit:  obj,
			commit: obj,
			repo:   repo,
		}, nil
	case *object.Tree:
		return &Tree{
			Type:    ObjectTypeTree,
			ID:      obj.ID().String(),
			ShortID: obj.ID().String()[:7],

			tree: obj,
			repo: repo,
		}, nil
	default:
		return nil, errors.New("Unknown object type")
	}
}

type Commit struct {
	Type      ObjectType `json:"type"`
	ID        string     `json:"id"`
	ShortID   string     `json:"shortId"`
	Raw       string     `json:"raw"`
	Tree      *Tree      `json:"tree"`
	Parents   []*Commit  `json:"parents"`

	commit *object.Commit
}

func (Commit) IsObject() {}

func (c *Commit) Message() string {
	return c.commit.Message
}

func (c *Commit) Author() *Signature {
	return &Signature{
		Name: c.commit.Author.Name,
		Email: c.commit.Author.Email,
		Time: c.commit.Author.When,
	}
}

func (c *Commit) Committer() *Signature {
	return &Signature{
		Name: c.commit.Committer.Name,
		Email: c.commit.Committer.Email,
		Time: c.commit.Committer.When,
	}
}

A graphql/graph/model/tree.go => graphql/graph/model/tree.go +87 -0
@@ 0,0 1,87 @@
package model

import (
	"sort"

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

type Tree struct {
	Type    ObjectType   `json:"type"`
	ID      string       `json:"id"`
	ShortID string       `json:"shortId"`
	Raw     string       `json:"raw"`

	tree *object.Tree
	repo *git.Repository
}

func (Tree) IsObject() {}

type TreeEntry struct {
	Name   string `json:"name"`
	Mode   int    `json:"mode"`

	hash plumbing.Hash
	repo *git.Repository
}

func (ent *TreeEntry) ID() string {
	return ent.hash.String()
}

func (ent *TreeEntry) Object() Object {
	obj, err := LookupObject(ent.repo, ent.hash)
	if err != nil {
		panic(err)
	}
	return obj
}

func (tree *Tree) Entries(count *int, next *string) []*TreeEntry {
	entries := tree.tree.Entries[:]
	sort.SliceStable(entries, func(a, b int) bool {
		return entries[a].Name < entries[b].Name
	})

	qlents := make([]*TreeEntry, len(entries))
	for i, ent := range entries {
		qlents[i] = &TreeEntry{
			Name: ent.Name,
			Mode: int(ent.Mode),
			hash: ent.Hash,
			repo: tree.repo,
		}
	}

	if next != nil {
		for i, ent := range qlents {
			if ent.Name == *next {
				qlents = qlents[i+1:]
				if len(entries) > *count {
					qlents = qlents[:*count]
				}
				return qlents
			}
		}
	}
	if len(qlents) > *count {
		qlents = qlents[:*count]
	}
	return qlents
}

func (tree *Tree) Entry(path string) *TreeEntry {
	ent, err := tree.tree.FindEntry(path)
	if err == object.ErrEntryNotFound {
		return nil
	}
	return &TreeEntry{
		Name: ent.Name,
		Mode: int(ent.Mode),
		hash: ent.Hash,
		repo: tree.repo,
	}
}

M graphql/graph/schema.graphqls => graphql/graph/schema.graphqls +15 -4
@@ 177,10 177,13 @@ type Tree implements Object {
  id: String!
  shortId: String!
  raw: String!
  entries: [TreeEntry!]!
  entries(count: Int = 100, next: String): [TreeEntry!]!

  entry(path: String): TreeEntry
}

type TreeEntry {
  id: String!
  name: String!
  object: Object!
  # Unix-style file mode, i.e. 0755 or 0644 (octal)


@@ 192,15 195,23 @@ enum BlobType {
  TEXT
}

type BinaryBlob {
  base64: String!
}

type TextBlob {
  text: String!
}

union BlobData = BinaryBlob | TextBlob

type Blob implements Object {
  type: ObjectType!
  id: String!
  shortId: String!
  raw: String!
  blobType: BlobType!
  # If blobType is BINARY, this will be base64 encoded; otherwise it will be
  # the blob's text contents verbatim
  data: String
  data: BlobData
}

type Tag implements Object {