forked from tangled.org/core
Monorepo for Tangled — https://tangled.org

lexicons: issue, issue state and comment

Custom lexicon for issues, issue state (open, closed) and issue comments.

The case with issue_at is a bit weird since we have a circular
dependency: the issue record requires the issue_id, and the issue entry
in the db requires the issue_at.

To resolve this we write to the db without the issue_at, fetch the
issue_id, create the issue record on the PDS, and then update the
issue_at (with SetIssueAt). It's not great, but whatever.

anirudh.fi 3f51a6bc 95c4e51c

verified
+980
api/tangled/cbor_gen.go
···
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
+
}
+27
api/tangled/issuecomment.go
···
+
// 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"`
+
}
+24
api/tangled/issuestate.go
···
+
// 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"`
+
}
+27
api/tangled/repoissue.go
···
+
// 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"`
+
}
+9
api/tangled/stateclosed.go
···
+
// 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"
+9
api/tangled/stateopen.go
···
+
// 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"
+3
appview/db/db.go
···
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
);
···
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),
+63 -11
appview/db/issues.go
···
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
···
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()
···
values (?, 1)
`, issue.RepoAt)
if err != nil {
-
return 0, err
+
return err
}
var nextId int
···
returning next_issue_id - 1
`, issue.RepoAt).Scan(&nextId)
if err != nil {
-
return 0, err
+
return err
}
issue.IssueId = nextId
···
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) {
···
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)
···
}
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,
···
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
}
···
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
}
+27 -35
appview/pages/templates/timeline.html
···
<h1>Timeline</h1>
{{ range .Timeline }}
-
<div class="relative
-
px-4
-
py-2
-
border-l
-
border-black
-
before:content-['']
-
before:absolute
-
before:w-1
-
before:h-1
-
before:bg-black
-
before:rounded-full
-
before:left-[-2.2px]
-
before:top-1/2
-
before:-translate-y-1/2
-
">
{{ if .Repo }}
-
{{ $userHandle := index $.DidHandleMap .Repo.Did }}
-
<div class="flex items-center">
-
<p class="text-gray-600">
-
<a href="/{{ $userHandle }}" class="no-underline hover:underline">{{ $userHandle }}</a>
-
created
-
<a href="/{{ $userHandle }}/{{ .Repo.Name }}" class="no-underline hover:underline">{{ .Repo.Name }}</a>
-
<time class="text-gray-700">{{ .Repo.Created | timeFmt }}</time>
-
</p>
-
</div>
+
<div class="border border-black p-4 m-2 bg-white w-1/2">
+
<div class="flex items-center">
+
<div class="text-sm text-gray-600">
+
{{ .Repo.Did }} created
+
</div>
+
div>
+
<div class="px-3">{{ .Repo.Name }}</div>
+
</div>
+
+
<time class="text-sm text-gray-700"
+
>{{ .Repo.Created | timeFmt }}</time
+
>
+
</div>
{{ else if .Follow }}
-
{{ $userHandle := index $.DidHandleMap .Follow.UserDid }}
-
{{ $subjectHandle := index $.DidHandleMap .Follow.SubjectDid }}
-
<div class="flex items-center">
-
<p class="text-gray-600">
-
<a href="/{{ $userHandle }}" class="no-underline hover:underline">{{ $userHandle }}</a>
-
followed
-
<a href="/{{ $subjectHandle }}" class="no-underline hover:underline">{{ $subjectHandle }}</a>
-
<time class="text-gray-700">{{ .Follow.FollowedAt | timeFmt }}</time>
-
</p>
-
</div>
+
<div class="border border-black p-4 m-2 bg-white w-1/2">
+
<div class="flex items-center">
+
<div class="text-sm text-gray-600">
+
{{ .Follow.UserDid }} followed
+
</div>
+
<div class="text-sm text-gray-800">
+
{{ .Follow.SubjectDid }}
+
</div>
+
</div>
+
+
<time class="text-sm text-gray-700"
+
>{{ .Follow.FollowedAt | timeFmt }}</time
+
>
+
</div>
{{ end }}
-
</div>
{{ end }}
{{ end }}
+106 -5
appview/state/repo.go
···
"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) {
···
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 {
···
}
commentId := rand.IntN(1000000)
-
fmt.Println(commentId)
-
fmt.Println("comment id", commentId)
err := s.db.NewComment(&db.Comment{
OwnerDid: user.Did,
···
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
}
···
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,
···
})
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
}
+3
cmd/gen.go
···
shtangled.KnotMember{},
shtangled.GraphFollow{},
shtangled.Repo{},
+
shtangled.RepoIssue{},
+
shtangled.RepoIssueState{},
+
shtangled.RepoIssueComment{},
); err != nil {
panic(err)
}
-1
go.mod
···
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
)
-2
go.sum
···
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=
+12
lexicons/issue/closed.json
···
+
{
+
"lexicon": 1,
+
"id": "sh.tangled.repo.issue.state.closed",
+
"needsCbor": true,
+
"needsType": true,
+
"defs": {
+
"main": {
+
"type": "token",
+
"description": "closed issue"
+
}
+
}
+
}
+40
lexicons/issue/comment.json
···
+
{
+
"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"
+
}
+
}
+
}
+
}
+
}
+
}
+39
lexicons/issue/issue.json
···
+
{
+
"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"
+
}
+
}
+
}
+
}
+
}
+
}
+12
lexicons/issue/open.json
···
+
{
+
"lexicon": 1,
+
"id": "sh.tangled.repo.issue.state.open",
+
"needsCbor": true,
+
"needsType": true,
+
"defs": {
+
"main": {
+
"type": "token",
+
"description": "open issue"
+
}
+
}
+
}
+31
lexicons/issue/state.json
···
+
{
+
"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"
+
}
+
}
+
}
+
}
+
}
+
}
+1 -5
lexicons/repo.json
···
"key": "tid",
"record": {
"type": "object",
-
"required": [
-
"name",
-
"knot",
-
"owner"
-
],
+
"required": ["name", "knot", "owner"],
"properties": {
"name": {
"type": "string",