diff --git a/api/tangled/cbor_gen.go b/api/tangled/cbor_gen.go --- a/api/tangled/cbor_gen.go +++ b/api/tangled/cbor_gen.go @@ -858,3 +858,983 @@ } return nil } +func (t *RepoIssue) MarshalCBOR(w io.Writer) error { + if t == nil { + _, err := w.Write(cbg.CborNull) + return err + } + + cw := cbg.NewCborWriter(w) + fieldCount := 7 + + if t.Body == nil { + fieldCount-- + } + + if t.CreatedAt == nil { + fieldCount-- + } + + if _, err := cw.Write(cbg.CborEncodeMajorType(cbg.MajMap, uint64(fieldCount))); err != nil { + return err + } + + // t.Body (string) (string) + if t.Body != nil { + + if len("body") > 1000000 { + return xerrors.Errorf("Value in field \"body\" was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("body"))); err != nil { + return err + } + if _, err := cw.WriteString(string("body")); err != nil { + return err + } + + if t.Body == nil { + if _, err := cw.Write(cbg.CborNull); err != nil { + return err + } + } else { + if len(*t.Body) > 1000000 { + return xerrors.Errorf("Value in field t.Body was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(*t.Body))); err != nil { + return err + } + if _, err := cw.WriteString(string(*t.Body)); err != nil { + return err + } + } + } + + // t.Repo (string) (string) + if len("repo") > 1000000 { + return xerrors.Errorf("Value in field \"repo\" was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("repo"))); err != nil { + return err + } + if _, err := cw.WriteString(string("repo")); err != nil { + return err + } + + if len(t.Repo) > 1000000 { + return xerrors.Errorf("Value in field t.Repo was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.Repo))); err != nil { + return err + } + if _, err := cw.WriteString(string(t.Repo)); err != nil { + return err + } + + // t.LexiconTypeID (string) (string) + if len("$type") > 1000000 { + return xerrors.Errorf("Value in field \"$type\" was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("$type"))); err != nil { + return err + } + if _, err := cw.WriteString(string("$type")); err != nil { + return err + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("sh.tangled.repo.issue"))); err != nil { + return err + } + if _, err := cw.WriteString(string("sh.tangled.repo.issue")); err != nil { + return err + } + + // t.Owner (string) (string) + if len("owner") > 1000000 { + return xerrors.Errorf("Value in field \"owner\" was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("owner"))); err != nil { + return err + } + if _, err := cw.WriteString(string("owner")); err != nil { + return err + } + + if len(t.Owner) > 1000000 { + return xerrors.Errorf("Value in field t.Owner was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.Owner))); err != nil { + return err + } + if _, err := cw.WriteString(string(t.Owner)); err != nil { + return err + } + + // t.Title (string) (string) + if len("title") > 1000000 { + return xerrors.Errorf("Value in field \"title\" was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("title"))); err != nil { + return err + } + if _, err := cw.WriteString(string("title")); err != nil { + return err + } + + if len(t.Title) > 1000000 { + return xerrors.Errorf("Value in field t.Title was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.Title))); err != nil { + return err + } + if _, err := cw.WriteString(string(t.Title)); err != nil { + return err + } + + // t.IssueId (int64) (int64) + if len("issueId") > 1000000 { + return xerrors.Errorf("Value in field \"issueId\" was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("issueId"))); err != nil { + return err + } + if _, err := cw.WriteString(string("issueId")); err != nil { + return err + } + + if t.IssueId >= 0 { + if err := cw.WriteMajorTypeHeader(cbg.MajUnsignedInt, uint64(t.IssueId)); err != nil { + return err + } + } else { + if err := cw.WriteMajorTypeHeader(cbg.MajNegativeInt, uint64(-t.IssueId-1)); err != nil { + return err + } + } + + // t.CreatedAt (string) (string) + if t.CreatedAt != nil { + + if len("createdAt") > 1000000 { + return xerrors.Errorf("Value in field \"createdAt\" was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("createdAt"))); err != nil { + return err + } + if _, err := cw.WriteString(string("createdAt")); err != nil { + return err + } + + if t.CreatedAt == nil { + if _, err := cw.Write(cbg.CborNull); err != nil { + return err + } + } else { + if len(*t.CreatedAt) > 1000000 { + return xerrors.Errorf("Value in field t.CreatedAt was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(*t.CreatedAt))); err != nil { + return err + } + if _, err := cw.WriteString(string(*t.CreatedAt)); err != nil { + return err + } + } + } + return nil +} + +func (t *RepoIssue) UnmarshalCBOR(r io.Reader) (err error) { + *t = RepoIssue{} + + cr := cbg.NewCborReader(r) + + maj, extra, err := cr.ReadHeader() + if err != nil { + return err + } + defer func() { + if err == io.EOF { + err = io.ErrUnexpectedEOF + } + }() + + if maj != cbg.MajMap { + return fmt.Errorf("cbor input should be of type map") + } + + if extra > cbg.MaxLength { + return fmt.Errorf("RepoIssue: map struct too large (%d)", extra) + } + + n := extra + + nameBuf := make([]byte, 9) + for i := uint64(0); i < n; i++ { + nameLen, ok, err := cbg.ReadFullStringIntoBuf(cr, nameBuf, 1000000) + if err != nil { + return err + } + + if !ok { + // Field doesn't exist on this type, so ignore it + if err := cbg.ScanForLinks(cr, func(cid.Cid) {}); err != nil { + return err + } + continue + } + + switch string(nameBuf[:nameLen]) { + // t.Body (string) (string) + case "body": + + { + b, err := cr.ReadByte() + if err != nil { + return err + } + if b != cbg.CborNull[0] { + if err := cr.UnreadByte(); err != nil { + return err + } + + sval, err := cbg.ReadStringWithMax(cr, 1000000) + if err != nil { + return err + } + + t.Body = (*string)(&sval) + } + } + // t.Repo (string) (string) + case "repo": + + { + sval, err := cbg.ReadStringWithMax(cr, 1000000) + if err != nil { + return err + } + + t.Repo = string(sval) + } + // t.LexiconTypeID (string) (string) + case "$type": + + { + sval, err := cbg.ReadStringWithMax(cr, 1000000) + if err != nil { + return err + } + + t.LexiconTypeID = string(sval) + } + // t.Owner (string) (string) + case "owner": + + { + sval, err := cbg.ReadStringWithMax(cr, 1000000) + if err != nil { + return err + } + + t.Owner = string(sval) + } + // t.Title (string) (string) + case "title": + + { + sval, err := cbg.ReadStringWithMax(cr, 1000000) + if err != nil { + return err + } + + t.Title = string(sval) + } + // t.IssueId (int64) (int64) + case "issueId": + { + maj, extra, err := cr.ReadHeader() + if err != nil { + return err + } + var extraI int64 + switch maj { + case cbg.MajUnsignedInt: + extraI = int64(extra) + if extraI < 0 { + return fmt.Errorf("int64 positive overflow") + } + case cbg.MajNegativeInt: + extraI = int64(extra) + if extraI < 0 { + return fmt.Errorf("int64 negative overflow") + } + extraI = -1 - extraI + default: + return fmt.Errorf("wrong type for int64 field: %d", maj) + } + + t.IssueId = int64(extraI) + } + // t.CreatedAt (string) (string) + case "createdAt": + + { + b, err := cr.ReadByte() + if err != nil { + return err + } + if b != cbg.CborNull[0] { + if err := cr.UnreadByte(); err != nil { + return err + } + + sval, err := cbg.ReadStringWithMax(cr, 1000000) + if err != nil { + return err + } + + t.CreatedAt = (*string)(&sval) + } + } + + default: + // Field doesn't exist on this type, so ignore it + if err := cbg.ScanForLinks(r, func(cid.Cid) {}); err != nil { + return err + } + } + } + + return nil +} +func (t *RepoIssueState) MarshalCBOR(w io.Writer) error { + if t == nil { + _, err := w.Write(cbg.CborNull) + return err + } + + cw := cbg.NewCborWriter(w) + fieldCount := 3 + + if t.State == nil { + fieldCount-- + } + + if _, err := cw.Write(cbg.CborEncodeMajorType(cbg.MajMap, uint64(fieldCount))); err != nil { + return err + } + + // t.LexiconTypeID (string) (string) + if len("$type") > 1000000 { + return xerrors.Errorf("Value in field \"$type\" was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("$type"))); err != nil { + return err + } + if _, err := cw.WriteString(string("$type")); err != nil { + return err + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("sh.tangled.repo.issue.state"))); err != nil { + return err + } + if _, err := cw.WriteString(string("sh.tangled.repo.issue.state")); err != nil { + return err + } + + // t.Issue (string) (string) + if len("issue") > 1000000 { + return xerrors.Errorf("Value in field \"issue\" was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("issue"))); err != nil { + return err + } + if _, err := cw.WriteString(string("issue")); err != nil { + return err + } + + if len(t.Issue) > 1000000 { + return xerrors.Errorf("Value in field t.Issue was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.Issue))); err != nil { + return err + } + if _, err := cw.WriteString(string(t.Issue)); err != nil { + return err + } + + // t.State (string) (string) + if t.State != nil { + + if len("state") > 1000000 { + return xerrors.Errorf("Value in field \"state\" was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("state"))); err != nil { + return err + } + if _, err := cw.WriteString(string("state")); err != nil { + return err + } + + if t.State == nil { + if _, err := cw.Write(cbg.CborNull); err != nil { + return err + } + } else { + if len(*t.State) > 1000000 { + return xerrors.Errorf("Value in field t.State was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(*t.State))); err != nil { + return err + } + if _, err := cw.WriteString(string(*t.State)); err != nil { + return err + } + } + } + return nil +} + +func (t *RepoIssueState) UnmarshalCBOR(r io.Reader) (err error) { + *t = RepoIssueState{} + + cr := cbg.NewCborReader(r) + + maj, extra, err := cr.ReadHeader() + if err != nil { + return err + } + defer func() { + if err == io.EOF { + err = io.ErrUnexpectedEOF + } + }() + + if maj != cbg.MajMap { + return fmt.Errorf("cbor input should be of type map") + } + + if extra > cbg.MaxLength { + return fmt.Errorf("RepoIssueState: map struct too large (%d)", extra) + } + + n := extra + + nameBuf := make([]byte, 5) + for i := uint64(0); i < n; i++ { + nameLen, ok, err := cbg.ReadFullStringIntoBuf(cr, nameBuf, 1000000) + if err != nil { + return err + } + + if !ok { + // Field doesn't exist on this type, so ignore it + if err := cbg.ScanForLinks(cr, func(cid.Cid) {}); err != nil { + return err + } + continue + } + + switch string(nameBuf[:nameLen]) { + // t.LexiconTypeID (string) (string) + case "$type": + + { + sval, err := cbg.ReadStringWithMax(cr, 1000000) + if err != nil { + return err + } + + t.LexiconTypeID = string(sval) + } + // t.Issue (string) (string) + case "issue": + + { + sval, err := cbg.ReadStringWithMax(cr, 1000000) + if err != nil { + return err + } + + t.Issue = string(sval) + } + // t.State (string) (string) + case "state": + + { + b, err := cr.ReadByte() + if err != nil { + return err + } + if b != cbg.CborNull[0] { + if err := cr.UnreadByte(); err != nil { + return err + } + + sval, err := cbg.ReadStringWithMax(cr, 1000000) + if err != nil { + return err + } + + t.State = (*string)(&sval) + } + } + + default: + // Field doesn't exist on this type, so ignore it + if err := cbg.ScanForLinks(r, func(cid.Cid) {}); err != nil { + return err + } + } + } + + return nil +} +func (t *RepoIssueComment) MarshalCBOR(w io.Writer) error { + if t == nil { + _, err := w.Write(cbg.CborNull) + return err + } + + cw := cbg.NewCborWriter(w) + fieldCount := 7 + + if t.Body == nil { + fieldCount-- + } + + if t.CommentId == nil { + fieldCount-- + } + + if t.CreatedAt == nil { + fieldCount-- + } + + if t.Owner == nil { + fieldCount-- + } + + if t.Repo == nil { + fieldCount-- + } + + if _, err := cw.Write(cbg.CborEncodeMajorType(cbg.MajMap, uint64(fieldCount))); err != nil { + return err + } + + // t.Body (string) (string) + if t.Body != nil { + + if len("body") > 1000000 { + return xerrors.Errorf("Value in field \"body\" was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("body"))); err != nil { + return err + } + if _, err := cw.WriteString(string("body")); err != nil { + return err + } + + if t.Body == nil { + if _, err := cw.Write(cbg.CborNull); err != nil { + return err + } + } else { + if len(*t.Body) > 1000000 { + return xerrors.Errorf("Value in field t.Body was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(*t.Body))); err != nil { + return err + } + if _, err := cw.WriteString(string(*t.Body)); err != nil { + return err + } + } + } + + // t.Repo (string) (string) + if t.Repo != nil { + + if len("repo") > 1000000 { + return xerrors.Errorf("Value in field \"repo\" was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("repo"))); err != nil { + return err + } + if _, err := cw.WriteString(string("repo")); err != nil { + return err + } + + if t.Repo == nil { + if _, err := cw.Write(cbg.CborNull); err != nil { + return err + } + } else { + if len(*t.Repo) > 1000000 { + return xerrors.Errorf("Value in field t.Repo was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(*t.Repo))); err != nil { + return err + } + if _, err := cw.WriteString(string(*t.Repo)); err != nil { + return err + } + } + } + + // t.LexiconTypeID (string) (string) + if len("$type") > 1000000 { + return xerrors.Errorf("Value in field \"$type\" was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("$type"))); err != nil { + return err + } + if _, err := cw.WriteString(string("$type")); err != nil { + return err + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("sh.tangled.repo.issue.comment"))); err != nil { + return err + } + if _, err := cw.WriteString(string("sh.tangled.repo.issue.comment")); err != nil { + return err + } + + // t.Issue (string) (string) + if len("issue") > 1000000 { + return xerrors.Errorf("Value in field \"issue\" was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("issue"))); err != nil { + return err + } + if _, err := cw.WriteString(string("issue")); err != nil { + return err + } + + if len(t.Issue) > 1000000 { + return xerrors.Errorf("Value in field t.Issue was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.Issue))); err != nil { + return err + } + if _, err := cw.WriteString(string(t.Issue)); err != nil { + return err + } + + // t.Owner (string) (string) + if t.Owner != nil { + + if len("owner") > 1000000 { + return xerrors.Errorf("Value in field \"owner\" was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("owner"))); err != nil { + return err + } + if _, err := cw.WriteString(string("owner")); err != nil { + return err + } + + if t.Owner == nil { + if _, err := cw.Write(cbg.CborNull); err != nil { + return err + } + } else { + if len(*t.Owner) > 1000000 { + return xerrors.Errorf("Value in field t.Owner was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(*t.Owner))); err != nil { + return err + } + if _, err := cw.WriteString(string(*t.Owner)); err != nil { + return err + } + } + } + + // t.CommentId (int64) (int64) + if t.CommentId != nil { + + if len("commentId") > 1000000 { + return xerrors.Errorf("Value in field \"commentId\" was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("commentId"))); err != nil { + return err + } + if _, err := cw.WriteString(string("commentId")); err != nil { + return err + } + + if t.CommentId == nil { + if _, err := cw.Write(cbg.CborNull); err != nil { + return err + } + } else { + if *t.CommentId >= 0 { + if err := cw.WriteMajorTypeHeader(cbg.MajUnsignedInt, uint64(*t.CommentId)); err != nil { + return err + } + } else { + if err := cw.WriteMajorTypeHeader(cbg.MajNegativeInt, uint64(-*t.CommentId-1)); err != nil { + return err + } + } + } + + } + + // t.CreatedAt (string) (string) + if t.CreatedAt != nil { + + if len("createdAt") > 1000000 { + return xerrors.Errorf("Value in field \"createdAt\" was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("createdAt"))); err != nil { + return err + } + if _, err := cw.WriteString(string("createdAt")); err != nil { + return err + } + + if t.CreatedAt == nil { + if _, err := cw.Write(cbg.CborNull); err != nil { + return err + } + } else { + if len(*t.CreatedAt) > 1000000 { + return xerrors.Errorf("Value in field t.CreatedAt was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(*t.CreatedAt))); err != nil { + return err + } + if _, err := cw.WriteString(string(*t.CreatedAt)); err != nil { + return err + } + } + } + return nil +} + +func (t *RepoIssueComment) UnmarshalCBOR(r io.Reader) (err error) { + *t = RepoIssueComment{} + + cr := cbg.NewCborReader(r) + + maj, extra, err := cr.ReadHeader() + if err != nil { + return err + } + defer func() { + if err == io.EOF { + err = io.ErrUnexpectedEOF + } + }() + + if maj != cbg.MajMap { + return fmt.Errorf("cbor input should be of type map") + } + + if extra > cbg.MaxLength { + return fmt.Errorf("RepoIssueComment: map struct too large (%d)", extra) + } + + n := extra + + nameBuf := make([]byte, 9) + for i := uint64(0); i < n; i++ { + nameLen, ok, err := cbg.ReadFullStringIntoBuf(cr, nameBuf, 1000000) + if err != nil { + return err + } + + if !ok { + // Field doesn't exist on this type, so ignore it + if err := cbg.ScanForLinks(cr, func(cid.Cid) {}); err != nil { + return err + } + continue + } + + switch string(nameBuf[:nameLen]) { + // t.Body (string) (string) + case "body": + + { + b, err := cr.ReadByte() + if err != nil { + return err + } + if b != cbg.CborNull[0] { + if err := cr.UnreadByte(); err != nil { + return err + } + + sval, err := cbg.ReadStringWithMax(cr, 1000000) + if err != nil { + return err + } + + t.Body = (*string)(&sval) + } + } + // t.Repo (string) (string) + case "repo": + + { + b, err := cr.ReadByte() + if err != nil { + return err + } + if b != cbg.CborNull[0] { + if err := cr.UnreadByte(); err != nil { + return err + } + + sval, err := cbg.ReadStringWithMax(cr, 1000000) + if err != nil { + return err + } + + t.Repo = (*string)(&sval) + } + } + // t.LexiconTypeID (string) (string) + case "$type": + + { + sval, err := cbg.ReadStringWithMax(cr, 1000000) + if err != nil { + return err + } + + t.LexiconTypeID = string(sval) + } + // t.Issue (string) (string) + case "issue": + + { + sval, err := cbg.ReadStringWithMax(cr, 1000000) + if err != nil { + return err + } + + t.Issue = string(sval) + } + // t.Owner (string) (string) + case "owner": + + { + b, err := cr.ReadByte() + if err != nil { + return err + } + if b != cbg.CborNull[0] { + if err := cr.UnreadByte(); err != nil { + return err + } + + sval, err := cbg.ReadStringWithMax(cr, 1000000) + if err != nil { + return err + } + + t.Owner = (*string)(&sval) + } + } + // t.CommentId (int64) (int64) + case "commentId": + { + + b, err := cr.ReadByte() + if err != nil { + return err + } + if b != cbg.CborNull[0] { + if err := cr.UnreadByte(); err != nil { + return err + } + maj, extra, err := cr.ReadHeader() + if err != nil { + return err + } + var extraI int64 + switch maj { + case cbg.MajUnsignedInt: + extraI = int64(extra) + if extraI < 0 { + return fmt.Errorf("int64 positive overflow") + } + case cbg.MajNegativeInt: + extraI = int64(extra) + if extraI < 0 { + return fmt.Errorf("int64 negative overflow") + } + extraI = -1 - extraI + default: + return fmt.Errorf("wrong type for int64 field: %d", maj) + } + + t.CommentId = (*int64)(&extraI) + } + } + // t.CreatedAt (string) (string) + case "createdAt": + + { + b, err := cr.ReadByte() + if err != nil { + return err + } + if b != cbg.CborNull[0] { + if err := cr.UnreadByte(); err != nil { + return err + } + + sval, err := cbg.ReadStringWithMax(cr, 1000000) + if err != nil { + return err + } + + t.CreatedAt = (*string)(&sval) + } + } + + default: + // Field doesn't exist on this type, so ignore it + if err := cbg.ScanForLinks(r, func(cid.Cid) {}); err != nil { + return err + } + } + } + + return nil +} diff --git a/api/tangled/issuecomment.go b/api/tangled/issuecomment.go new file mode 100644 --- /dev/null +++ b/api/tangled/issuecomment.go @@ -0,0 +1,27 @@ +// Code generated by cmd/lexgen (see Makefile's lexgen); DO NOT EDIT. + +package tangled + +// schema: sh.tangled.repo.issue.comment + +import ( + "github.com/bluesky-social/indigo/lex/util" +) + +const ( + RepoIssueCommentNSID = "sh.tangled.repo.issue.comment" +) + +func init() { + util.RegisterType("sh.tangled.repo.issue.comment", &RepoIssueComment{}) +} // +// RECORDTYPE: RepoIssueComment +type RepoIssueComment struct { + LexiconTypeID string `json:"$type,const=sh.tangled.repo.issue.comment" cborgen:"$type,const=sh.tangled.repo.issue.comment"` + Body *string `json:"body,omitempty" cborgen:"body,omitempty"` + CommentId *int64 `json:"commentId,omitempty" cborgen:"commentId,omitempty"` + CreatedAt *string `json:"createdAt,omitempty" cborgen:"createdAt,omitempty"` + Issue string `json:"issue" cborgen:"issue"` + Owner *string `json:"owner,omitempty" cborgen:"owner,omitempty"` + Repo *string `json:"repo,omitempty" cborgen:"repo,omitempty"` +} diff --git a/api/tangled/issuestate.go b/api/tangled/issuestate.go new file mode 100644 --- /dev/null +++ b/api/tangled/issuestate.go @@ -0,0 +1,24 @@ +// Code generated by cmd/lexgen (see Makefile's lexgen); DO NOT EDIT. + +package tangled + +// schema: sh.tangled.repo.issue.state + +import ( + "github.com/bluesky-social/indigo/lex/util" +) + +const ( + RepoIssueStateNSID = "sh.tangled.repo.issue.state" +) + +func init() { + util.RegisterType("sh.tangled.repo.issue.state", &RepoIssueState{}) +} // +// RECORDTYPE: RepoIssueState +type RepoIssueState struct { + LexiconTypeID string `json:"$type,const=sh.tangled.repo.issue.state" cborgen:"$type,const=sh.tangled.repo.issue.state"` + Issue string `json:"issue" cborgen:"issue"` + // state: state of the issue + State *string `json:"state,omitempty" cborgen:"state,omitempty"` +} diff --git a/api/tangled/repoissue.go b/api/tangled/repoissue.go new file mode 100644 --- /dev/null +++ b/api/tangled/repoissue.go @@ -0,0 +1,27 @@ +// Code generated by cmd/lexgen (see Makefile's lexgen); DO NOT EDIT. + +package tangled + +// schema: sh.tangled.repo.issue + +import ( + "github.com/bluesky-social/indigo/lex/util" +) + +const ( + RepoIssueNSID = "sh.tangled.repo.issue" +) + +func init() { + util.RegisterType("sh.tangled.repo.issue", &RepoIssue{}) +} // +// RECORDTYPE: RepoIssue +type RepoIssue struct { + LexiconTypeID string `json:"$type,const=sh.tangled.repo.issue" cborgen:"$type,const=sh.tangled.repo.issue"` + Body *string `json:"body,omitempty" cborgen:"body,omitempty"` + CreatedAt *string `json:"createdAt,omitempty" cborgen:"createdAt,omitempty"` + IssueId int64 `json:"issueId" cborgen:"issueId"` + Owner string `json:"owner" cborgen:"owner"` + Repo string `json:"repo" cborgen:"repo"` + Title string `json:"title" cborgen:"title"` +} diff --git a/api/tangled/stateclosed.go b/api/tangled/stateclosed.go new file mode 100644 --- /dev/null +++ b/api/tangled/stateclosed.go @@ -0,0 +1,9 @@ +// Code generated by cmd/lexgen (see Makefile's lexgen); DO NOT EDIT. + +package tangled + +// schema: sh.tangled.repo.issue.state.closed + +const () + +const RepoIssueStateClosed = "sh.tangled.repo.issue.state.closed" diff --git a/api/tangled/stateopen.go b/api/tangled/stateopen.go new file mode 100644 --- /dev/null +++ b/api/tangled/stateopen.go @@ -0,0 +1,9 @@ +// Code generated by cmd/lexgen (see Makefile's lexgen); DO NOT EDIT. + +package tangled + +// schema: sh.tangled.repo.issue.state.open + +const () + +const RepoIssueStateOpen = "sh.tangled.repo.issue.state.open" diff --git a/appview/db/db.go b/appview/db/db.go --- a/appview/db/db.go +++ b/appview/db/db.go @@ -73,7 +73,9 @@ repo_at text not null, issue_id integer not null unique, title text not null, body text not null, + open integer not null default 1, created text not null default (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')), + issue_at text, unique(repo_at, issue_id), foreign key (repo_at) references repos(at_uri) on delete cascade ); @@ -83,6 +85,7 @@ owner_did text not null, issue_id integer not null, repo_at text not null, comment_id integer not null, + comment_at text not null, body text not null, created text not null default (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')), unique(issue_id, comment_id), diff --git a/appview/db/issues.go b/appview/db/issues.go --- a/appview/db/issues.go +++ b/appview/db/issues.go @@ -1,11 +1,15 @@ package db -import "time" +import ( + "database/sql" + "time" +) type Issue struct { RepoAt string OwnerDid string IssueId int + IssueAt string Created *time.Time Title string Body string @@ -15,16 +19,17 @@ type Comment struct { OwnerDid string RepoAt string + CommentAt string Issue int CommentId int Body string Created *time.Time } -func (d *DB) NewIssue(issue *Issue) (int, error) { +func (d *DB) NewIssue(issue *Issue) error { tx, err := d.db.Begin() if err != nil { - return 0, err + return err } defer tx.Rollback() @@ -33,7 +38,7 @@ insert or ignore into repo_issue_seqs (repo_at, next_issue_id) values (?, 1) `, issue.RepoAt) if err != nil { - return 0, err + return err } var nextId int @@ -44,7 +49,7 @@ where repo_at = ? returning next_issue_id - 1 `, issue.RepoAt).Scan(&nextId) if err != nil { - return 0, err + return err } issue.IssueId = nextId @@ -54,14 +59,37 @@ insert into issues (repo_at, owner_did, issue_id, title, body) values (?, ?, ?, ?, ?) `, issue.RepoAt, issue.OwnerDid, issue.IssueId, issue.Title, issue.Body) if err != nil { - return 0, err + return err } if err := tx.Commit(); err != nil { - return 0, err + return err } - return nextId, nil + return nil +} + +func (d *DB) SetIssueAt(repoAt string, issueId int, issueAt string) error { + _, err := d.db.Exec(`update issues set issue_at = ? where repo_at = ? and issue_id = ?`, issueAt, repoAt, issueId) + return err +} + +func (d *DB) GetIssueAt(repoAt string, issueId int) (string, error) { + var issueAt string + err := d.db.QueryRow(`select issue_at from issues where repo_at = ? and issue_id = ?`, repoAt, issueId).Scan(&issueAt) + return issueAt, err +} + +func (d *DB) GetIssueId(repoAt string) (int, error) { + var issueId int + err := d.db.QueryRow(`select next_issue_id from repo_issue_seqs where repo_at = ?`, repoAt).Scan(&issueId) + return issueId - 1, err +} + +func (d *DB) GetIssueOwnerDid(repoAt string, issueId int) (string, error) { + var ownerDid string + err := d.db.QueryRow(`select owner_did from issues where repo_at = ? and issue_id = ?`, repoAt, issueId).Scan(&ownerDid) + return ownerDid, err } func (d *DB) GetIssues(repoAt string) ([]Issue, error) { @@ -97,6 +125,26 @@ return issues, nil } +func (d *DB) GetIssue(repoAt string, issueId int) (*Issue, error) { + query := `select owner_did, created, title, body, open from issues where repo_at = ? and issue_id = ?` + row := d.db.QueryRow(query, repoAt, issueId) + + var issue Issue + var createdAt string + err := row.Scan(&issue.OwnerDid, &createdAt, &issue.Title, &issue.Body, &issue.Open) + if err != nil { + return nil, err + } + + createdTime, err := time.Parse(time.RFC3339, createdAt) + if err != nil { + return nil, err + } + issue.Created = &createdTime + + return &issue, nil +} + func (d *DB) GetIssueWithComments(repoAt string, issueId int) (*Issue, []Comment, error) { query := `select owner_did, issue_id, created, title, body, open from issues where repo_at = ? and issue_id = ?` row := d.db.QueryRow(query, repoAt, issueId) @@ -123,11 +171,12 @@ return &issue, comments, nil } func (d *DB) NewComment(comment *Comment) error { - query := `insert into comments (owner_did, repo_at, issue_id, comment_id, body) values (?, ?, ?, ?, ?)` + query := `insert into comments (owner_did, repo_at, comment_at, issue_id, comment_id, body) values (?, ?, ?, ?, ?, ?)` _, err := d.db.Exec( query, comment.OwnerDid, comment.RepoAt, + comment.CommentAt, comment.Issue, comment.CommentId, comment.Body, @@ -138,7 +187,10 @@ func (d *DB) GetComments(repoAt string, issueId int) ([]Comment, error) { var comments []Comment - rows, err := d.db.Query(`select owner_did, issue_id, comment_id, body, created from comments where repo_at = ? and issue_id = ? order by created asc`, repoAt, issueId) + rows, err := d.db.Query(`select owner_did, issue_id, comment_id, comment_at, body, created from comments where repo_at = ? and issue_id = ? order by created asc`, repoAt, issueId) + if err == sql.ErrNoRows { + return []Comment{}, nil + } if err != nil { return nil, err } @@ -147,7 +199,7 @@ for rows.Next() { var comment Comment var createdAt string - err := rows.Scan(&comment.OwnerDid, &comment.Issue, &comment.CommentId, &comment.Body, &createdAt) + err := rows.Scan(&comment.OwnerDid, &comment.Issue, &comment.CommentId, &comment.CommentAt, &comment.Body, &createdAt) if err != nil { return nil, err } diff --git a/appview/pages/templates/timeline.html b/appview/pages/templates/timeline.html --- a/appview/pages/templates/timeline.html +++ b/appview/pages/templates/timeline.html @@ -4,44 +4,36 @@ {{ define "content" }}

Timeline

{{ range .Timeline }} -
{{ if .Repo }} - {{ $userHandle := index $.DidHandleMap .Repo.Did }} -
-

- {{ $userHandle }} - created - {{ .Repo.Name }} - -

-
+
+
+
+ {{ .Repo.Did }} created +
+ div> +
{{ .Repo.Name }}
+
+ + +
{{ else if .Follow }} - {{ $userHandle := index $.DidHandleMap .Follow.UserDid }} - {{ $subjectHandle := index $.DidHandleMap .Follow.SubjectDid }} -
-

- {{ $userHandle }} - followed - {{ $subjectHandle }} - -

-
+
+
+
+ {{ .Follow.UserDid }} followed +
+
+ {{ .Follow.SubjectDid }} +
+
+ + +
{{ end }} -
{{ end }} {{ end }} diff --git a/appview/state/repo.go b/appview/state/repo.go --- a/appview/state/repo.go +++ b/appview/state/repo.go @@ -11,14 +11,19 @@ "net/http" "path" "strconv" "strings" + "time" "github.com/bluesky-social/indigo/atproto/identity" securejoin "github.com/cyphar/filepath-securejoin" "github.com/go-chi/chi/v5" + "github.com/sotangled/tangled/api/tangled" "github.com/sotangled/tangled/appview/auth" "github.com/sotangled/tangled/appview/db" "github.com/sotangled/tangled/appview/pages" "github.com/sotangled/tangled/types" + + comatproto "github.com/bluesky-social/indigo/api/atproto" + lexutil "github.com/bluesky-social/indigo/lex/util" ) func (s *State) RepoIndex(w http.ResponseWriter, r *http.Request) { @@ -564,13 +569,44 @@ log.Println("failed to parse issue id", err) return } + issue, err := s.db.GetIssue(f.RepoAt, issueIdInt) + if err != nil { + log.Println("failed to get issue", err) + s.pages.Notice(w, "issues", "Failed to close issue. Try again later.") + return + } + + // TODO: make this more granular if user.Did == f.OwnerDid() { + + closed := tangled.RepoIssueStateClosed + + client, _ := s.auth.AuthorizedClient(r) + _, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ + Collection: tangled.RepoIssueStateNSID, + Repo: issue.OwnerDid, + Rkey: s.TID(), + Record: &lexutil.LexiconTypeDecoder{ + Val: &tangled.RepoIssueState{ + Issue: issue.IssueAt, + State: &closed, + }, + }, + }) + + if err != nil { + log.Println("failed to update issue state", err) + s.pages.Notice(w, "issues", "Failed to close issue. Try again later.") + return + } + err := s.db.CloseIssue(f.RepoAt, issueIdInt) if err != nil { log.Println("failed to close issue", err) s.pages.Notice(w, "issues", "Failed to close issue. Try again later.") return } + s.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d", f.OwnerSlashRepo(), issueIdInt)) return } else { @@ -637,8 +673,6 @@ return } commentId := rand.IntN(1000000) - fmt.Println(commentId) - fmt.Println("comment id", commentId) err := s.db.NewComment(&db.Comment{ OwnerDid: user.Did, @@ -653,6 +687,38 @@ s.pages.Notice(w, "issue-comment", "Failed to create comment.") return } + createdAt := time.Now().Format(time.RFC3339) + commentIdInt64 := int64(commentId) + ownerDid := user.Did + issueAt, err := s.db.GetIssueAt(f.RepoAt, issueIdInt) + if err != nil { + log.Println("failed to get issue at", err) + s.pages.Notice(w, "issue-comment", "Failed to create comment.") + return + } + + client, _ := s.auth.AuthorizedClient(r) + _, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ + Collection: tangled.RepoIssueCommentNSID, + Repo: user.Did, + Rkey: s.TID(), + Record: &lexutil.LexiconTypeDecoder{ + Val: &tangled.RepoIssueComment{ + Repo: &f.RepoAt, + Issue: issueAt, + CommentId: &commentIdInt64, + Owner: &ownerDid, + Body: &body, + CreatedAt: &createdAt, + }, + }, + }) + if err != nil { + log.Println("failed to create comment", err) + s.pages.Notice(w, "issue-comment", "Failed to create comment.") + return + } + s.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d#comment-%d", f.OwnerSlashRepo(), issueIdInt, commentId)) return } @@ -711,11 +777,11 @@ title := r.FormValue("title") body := r.FormValue("body") if title == "" || body == "" { - s.pages.Notice(w, "issue", "Title and body are required") + s.pages.Notice(w, "issues", "Title and body are required") return } - issueId, err := s.db.NewIssue(&db.Issue{ + err = s.db.NewIssue(&db.Issue{ RepoAt: f.RepoAt, Title: title, Body: body, @@ -723,7 +789,42 @@ OwnerDid: user.Did, }) if err != nil { log.Println("failed to create issue", err) - s.pages.Notice(w, "issue", "Failed to create issue.") + s.pages.Notice(w, "issues", "Failed to create issue.") + return + } + + issueId, err := s.db.GetIssueId(f.RepoAt) + if err != nil { + log.Println("failed to get issue id", err) + s.pages.Notice(w, "issues", "Failed to create issue.") + return + } + + client, _ := s.auth.AuthorizedClient(r) + resp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ + Collection: tangled.RepoIssueNSID, + Repo: user.Did, + Rkey: s.TID(), + Record: &lexutil.LexiconTypeDecoder{ + Val: &tangled.RepoIssue{ + Repo: f.RepoAt, + Title: title, + Body: &body, + Owner: user.Did, + IssueId: int64(issueId), + }, + }, + }) + if err != nil { + log.Println("failed to create issue", err) + s.pages.Notice(w, "issues", "Failed to create issue.") + return + } + + err = s.db.SetIssueAt(f.RepoAt, issueId, resp.Uri) + if err != nil { + log.Println("failed to set issue at", err) + s.pages.Notice(w, "issues", "Failed to create issue.") return } diff --git a/cmd/gen.go b/cmd/gen.go --- a/cmd/gen.go +++ b/cmd/gen.go @@ -18,6 +18,9 @@ shtangled.PublicKey{}, shtangled.KnotMember{}, shtangled.GraphFollow{}, shtangled.Repo{}, + shtangled.RepoIssue{}, + shtangled.RepoIssueState{}, + shtangled.RepoIssueComment{}, ); err != nil { panic(err) } diff --git a/go.mod b/go.mod --- a/go.mod +++ b/go.mod @@ -24,7 +24,6 @@ github.com/microcosm-cc/bluemonday v1.0.27 github.com/russross/blackfriday/v2 v2.1.0 github.com/sethvargo/go-envconfig v1.1.0 github.com/whyrusleeping/cbor-gen v0.2.1-0.20241030202151-b7a6831be65e - golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8 golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 ) diff --git a/go.sum b/go.sum --- a/go.sum +++ b/go.sum @@ -307,8 +307,6 @@ golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= -golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8 h1:LoYXNGAShUG3m/ehNk4iFctuhGX/+R1ZpfJ4/ia80JM= -golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= diff --git a/lexicons/issue/closed.json b/lexicons/issue/closed.json new file mode 100644 --- /dev/null +++ b/lexicons/issue/closed.json @@ -0,0 +1,12 @@ +{ + "lexicon": 1, + "id": "sh.tangled.repo.issue.state.closed", + "needsCbor": true, + "needsType": true, + "defs": { + "main": { + "type": "token", + "description": "closed issue" + } + } +} diff --git a/lexicons/issue/comment.json b/lexicons/issue/comment.json new file mode 100644 --- /dev/null +++ b/lexicons/issue/comment.json @@ -0,0 +1,40 @@ +{ + "lexicon": 1, + "id": "sh.tangled.repo.issue.comment", + "needsCbor": true, + "needsType": true, + "defs": { + "main": { + "type": "record", + "key": "tid", + "record": { + "type": "object", + "required": ["issue"], + "properties": { + "issue": { + "type": "string", + "format": "at-uri" + }, + "repo": { + "type": "string", + "format": "at-uri" + }, + "commentId": { + "type": "integer" + }, + "owner": { + "type": "string", + "format": "did" + }, + "body": { + "type": "string" + }, + "createdAt": { + "type": "string", + "format": "datetime" + } + } + } + } + } +} diff --git a/lexicons/issue/issue.json b/lexicons/issue/issue.json new file mode 100644 --- /dev/null +++ b/lexicons/issue/issue.json @@ -0,0 +1,39 @@ +{ + "lexicon": 1, + "id": "sh.tangled.repo.issue", + "needsCbor": true, + "needsType": true, + "defs": { + "main": { + "type": "record", + "key": "tid", + "record": { + "type": "object", + "required": ["repo", "issueId", "owner", "title"], + "properties": { + "repo": { + "type": "string", + "format": "at-uri" + }, + "issueId": { + "type": "integer" + }, + "owner": { + "type": "string", + "format": "did" + }, + "title": { + "type": "string" + }, + "body": { + "type": "string" + }, + "createdAt": { + "type": "string", + "format": "datetime" + } + } + } + } + } +} diff --git a/lexicons/issue/open.json b/lexicons/issue/open.json new file mode 100644 --- /dev/null +++ b/lexicons/issue/open.json @@ -0,0 +1,12 @@ +{ + "lexicon": 1, + "id": "sh.tangled.repo.issue.state.open", + "needsCbor": true, + "needsType": true, + "defs": { + "main": { + "type": "token", + "description": "open issue" + } + } +} diff --git a/lexicons/issue/state.json b/lexicons/issue/state.json new file mode 100644 --- /dev/null +++ b/lexicons/issue/state.json @@ -0,0 +1,31 @@ +{ + "lexicon": 1, + "id": "sh.tangled.repo.issue.state", + "needsCbor": true, + "needsType": true, + "defs": { + "main": { + "type": "record", + "key": "tid", + "record": { + "type": "object", + "required": ["issue"], + "properties": { + "issue": { + "type": "string", + "format": "at-uri" + }, + "state": { + "type": "string", + "description": "state of the issue", + "knownValues": [ + "sh.tangled.repo.issue.state.open", + "sh.tangled.repo.issue.state.closed" + ], + "default": "sh.tangled.repo.issue.state.open" + } + } + } + } + } +} diff --git a/lexicons/repo.json b/lexicons/repo.json --- a/lexicons/repo.json +++ b/lexicons/repo.json @@ -9,11 +9,7 @@ "type": "record", "key": "tid", "record": { "type": "object", - "required": [ - "name", - "knot", - "owner" - ], + "required": ["name", "knot", "owner"], "properties": { "name": { "type": "string",