diff --git a/cmd/gen.go b/cmd/gen.go --- a/cmd/gen.go +++ b/cmd/gen.go @@ -14,13 +14,14 @@ if err := genCfg.WriteMapEncodersToFile( "api/tangled/cbor_gen.go", "tangled", - shtangled.PublicKey{}, - shtangled.KnotMember{}, + shtangled.FeedStar{}, shtangled.GraphFollow{}, - shtangled.Repo{}, - shtangled.RepoIssue{}, - shtangled.RepoIssueState{}, + shtangled.KnotMember{}, + shtangled.PublicKey{}, shtangled.RepoIssueComment{}, + shtangled.RepoIssueState{}, + shtangled.RepoIssue{}, + shtangled.Repo{}, ); err != nil { panic(err) } diff --git a/lexicons/star.json b/lexicons/star.json new file mode 100644 --- /dev/null +++ b/lexicons/star.json @@ -0,0 +1,31 @@ +{ + "lexicon": 1, + "id": "sh.tangled.feed.star", + "needsCbor": true, + "needsType": true, + "defs": { + "main": { + "type": "record", + "key": "tid", + "record": { + "type": "object", + "required": [ + "createdAt", + "subject" + ], + "properties": { + "createdAt": { + "type": "string", + "format": "datetime" + }, + "subject": { + "type": "string", + "format": "at-uri" + } + } + } + } + } +} + + 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 @@ -18,7 +18,7 @@ var _ = math.E var _ = sort.Sort -func (t *PublicKey) MarshalCBOR(w io.Writer) error { +func (t *FeedStar) MarshalCBOR(w io.Writer) error { if t == nil { _, err := w.Write(cbg.CborNull) return err @@ -26,53 +26,7 @@ cw := cbg.NewCborWriter(w) - if _, err := cw.Write([]byte{164}); err != nil { - return err - } - - // t.Key (string) (string) - if len("key") > 1000000 { - return xerrors.Errorf("Value in field \"key\" was too long") - } - - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("key"))); err != nil { - return err - } - if _, err := cw.WriteString(string("key")); err != nil { - return err - } - - if len(t.Key) > 1000000 { - return xerrors.Errorf("Value in field t.Key was too long") - } - - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.Key))); err != nil { - return err - } - if _, err := cw.WriteString(string(t.Key)); err != nil { - return err - } - - // t.Name (string) (string) - if len("name") > 1000000 { - return xerrors.Errorf("Value in field \"name\" was too long") - } - - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("name"))); err != nil { - return err - } - if _, err := cw.WriteString(string("name")); err != nil { - return err - } - - if len(t.Name) > 1000000 { - return xerrors.Errorf("Value in field t.Name was too long") - } - - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.Name))); err != nil { - return err - } - if _, err := cw.WriteString(string(t.Name)); err != nil { + if _, err := cw.Write([]byte{163}); err != nil { return err } @@ -88,40 +42,63 @@ return err } - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("sh.tangled.publicKey"))); err != nil { + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("sh.tangled.feed.star"))); err != nil { return err } - if _, err := cw.WriteString(string("sh.tangled.publicKey")); err != nil { - return err - } - - // t.Created (string) (string) - if len("created") > 1000000 { - return xerrors.Errorf("Value in field \"created\" was too long") - } - - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("created"))); err != nil { - return err - } - if _, err := cw.WriteString(string("created")); err != nil { + if _, err := cw.WriteString(string("sh.tangled.feed.star")); err != nil { return err } - if len(t.Created) > 1000000 { - return xerrors.Errorf("Value in field t.Created was too long") + // t.Subject (string) (string) + if len("subject") > 1000000 { + return xerrors.Errorf("Value in field \"subject\" was too long") } - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.Created))); err != nil { + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("subject"))); err != nil { return err } - if _, err := cw.WriteString(string(t.Created)); err != nil { + if _, err := cw.WriteString(string("subject")); err != nil { + return err + } + + if len(t.Subject) > 1000000 { + return xerrors.Errorf("Value in field t.Subject was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.Subject))); err != nil { + return err + } + if _, err := cw.WriteString(string(t.Subject)); err != nil { + return err + } + + // t.CreatedAt (string) (string) + 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 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 *PublicKey) UnmarshalCBOR(r io.Reader) (err error) { - *t = PublicKey{} +func (t *FeedStar) UnmarshalCBOR(r io.Reader) (err error) { + *t = FeedStar{} cr := cbg.NewCborReader(r) @@ -140,12 +117,12 @@ } if extra > cbg.MaxLength { - return fmt.Errorf("PublicKey: map struct too large (%d)", extra) + return fmt.Errorf("FeedStar: map struct too large (%d)", extra) } n := extra - nameBuf := make([]byte, 7) + nameBuf := make([]byte, 9) for i := uint64(0); i < n; i++ { nameLen, ok, err := cbg.ReadFullStringIntoBuf(cr, nameBuf, 1000000) if err != nil { @@ -161,29 +138,7 @@ } switch string(nameBuf[:nameLen]) { - // t.Key (string) (string) - case "key": - - { - sval, err := cbg.ReadStringWithMax(cr, 1000000) - if err != nil { - return err - } - - t.Key = string(sval) - } - // t.Name (string) (string) - case "name": - - { - sval, err := cbg.ReadStringWithMax(cr, 1000000) - if err != nil { - return err - } - - t.Name = string(sval) - } - // t.LexiconTypeID (string) (string) + // t.LexiconTypeID (string) (string) case "$type": { @@ -194,8 +149,8 @@ t.LexiconTypeID = string(sval) } - // t.Created (string) (string) - case "created": + // t.Subject (string) (string) + case "subject": { sval, err := cbg.ReadStringWithMax(cr, 1000000) @@ -203,7 +158,182 @@ return err } - t.Created = string(sval) + t.Subject = string(sval) + } + // t.CreatedAt (string) (string) + case "createdAt": + + { + 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 *GraphFollow) MarshalCBOR(w io.Writer) error { + if t == nil { + _, err := w.Write(cbg.CborNull) + return err + } + + cw := cbg.NewCborWriter(w) + + if _, err := cw.Write([]byte{163}); 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.graph.follow"))); err != nil { + return err + } + if _, err := cw.WriteString(string("sh.tangled.graph.follow")); err != nil { + return err + } + + // t.Subject (string) (string) + if len("subject") > 1000000 { + return xerrors.Errorf("Value in field \"subject\" was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("subject"))); err != nil { + return err + } + if _, err := cw.WriteString(string("subject")); err != nil { + return err + } + + if len(t.Subject) > 1000000 { + return xerrors.Errorf("Value in field t.Subject was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.Subject))); err != nil { + return err + } + if _, err := cw.WriteString(string(t.Subject)); err != nil { + return err + } + + // t.CreatedAt (string) (string) + 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 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 *GraphFollow) UnmarshalCBOR(r io.Reader) (err error) { + *t = GraphFollow{} + + 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("GraphFollow: 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.LexiconTypeID (string) (string) + case "$type": + + { + sval, err := cbg.ReadStringWithMax(cr, 1000000) + if err != nil { + return err + } + + t.LexiconTypeID = string(sval) + } + // t.Subject (string) (string) + case "subject": + + { + sval, err := cbg.ReadStringWithMax(cr, 1000000) + if err != nil { + return err + } + + t.Subject = string(sval) + } + // t.CreatedAt (string) (string) + case "createdAt": + + { + sval, err := cbg.ReadStringWithMax(cr, 1000000) + if err != nil { + return err + } + + t.CreatedAt = string(sval) } default: @@ -438,7 +568,7 @@ return nil } -func (t *GraphFollow) MarshalCBOR(w io.Writer) error { +func (t *PublicKey) MarshalCBOR(w io.Writer) error { if t == nil { _, err := w.Write(cbg.CborNull) return err @@ -446,199 +576,30 @@ cw := cbg.NewCborWriter(w) - if _, err := cw.Write([]byte{163}); err != nil { + if _, err := cw.Write([]byte{164}); err != nil { return err } - // t.LexiconTypeID (string) (string) - if len("$type") > 1000000 { - return xerrors.Errorf("Value in field \"$type\" was too long") + // t.Key (string) (string) + if len("key") > 1000000 { + return xerrors.Errorf("Value in field \"key\" was too long") } - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("$type"))); err != nil { + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("key"))); err != nil { return err } - if _, err := cw.WriteString(string("$type")); err != nil { - return err - } - - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("sh.tangled.graph.follow"))); err != nil { - return err - } - if _, err := cw.WriteString(string("sh.tangled.graph.follow")); err != nil { + if _, err := cw.WriteString(string("key")); err != nil { return err } - // t.Subject (string) (string) - if len("subject") > 1000000 { - return xerrors.Errorf("Value in field \"subject\" was too long") + if len(t.Key) > 1000000 { + return xerrors.Errorf("Value in field t.Key was too long") } - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("subject"))); err != nil { + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.Key))); err != nil { return err } - if _, err := cw.WriteString(string("subject")); err != nil { - return err - } - - if len(t.Subject) > 1000000 { - return xerrors.Errorf("Value in field t.Subject was too long") - } - - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.Subject))); err != nil { - return err - } - if _, err := cw.WriteString(string(t.Subject)); err != nil { - return err - } - - // t.CreatedAt (string) (string) - 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 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 *GraphFollow) UnmarshalCBOR(r io.Reader) (err error) { - *t = GraphFollow{} - - 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("GraphFollow: 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.LexiconTypeID (string) (string) - case "$type": - - { - sval, err := cbg.ReadStringWithMax(cr, 1000000) - if err != nil { - return err - } - - t.LexiconTypeID = string(sval) - } - // t.Subject (string) (string) - case "subject": - - { - sval, err := cbg.ReadStringWithMax(cr, 1000000) - if err != nil { - return err - } - - t.Subject = string(sval) - } - // t.CreatedAt (string) (string) - case "createdAt": - - { - 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 *Repo) MarshalCBOR(w io.Writer) error { - if t == nil { - _, err := w.Write(cbg.CborNull) - return err - } - - cw := cbg.NewCborWriter(w) - fieldCount := 5 - - if t.AddedAt == nil { - fieldCount-- - } - - if _, err := cw.Write(cbg.CborEncodeMajorType(cbg.MajMap, uint64(fieldCount))); err != nil { - return err - } - - // t.Knot (string) (string) - if len("knot") > 1000000 { - return xerrors.Errorf("Value in field \"knot\" was too long") - } - - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("knot"))); err != nil { - return err - } - if _, err := cw.WriteString(string("knot")); err != nil { - return err - } - - if len(t.Knot) > 1000000 { - return xerrors.Errorf("Value in field t.Knot was too long") - } - - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.Knot))); err != nil { - return err - } - if _, err := cw.WriteString(string(t.Knot)); err != nil { + if _, err := cw.WriteString(string(t.Key)); err != nil { return err } @@ -677,72 +638,40 @@ return err } - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("sh.tangled.repo"))); err != nil { + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("sh.tangled.publicKey"))); err != nil { return err } - if _, err := cw.WriteString(string("sh.tangled.repo")); 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 { + if _, err := cw.WriteString(string("sh.tangled.publicKey")); err != nil { return err } - if len(t.Owner) > 1000000 { - return xerrors.Errorf("Value in field t.Owner was too long") + // t.Created (string) (string) + if len("created") > 1000000 { + return xerrors.Errorf("Value in field \"created\" was too long") } - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.Owner))); err != nil { + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("created"))); err != nil { return err } - if _, err := cw.WriteString(string(t.Owner)); err != nil { + if _, err := cw.WriteString(string("created")); err != nil { return err } - // t.AddedAt (string) (string) - if t.AddedAt != nil { + if len(t.Created) > 1000000 { + return xerrors.Errorf("Value in field t.Created was too long") + } - if len("addedAt") > 1000000 { - return xerrors.Errorf("Value in field \"addedAt\" was too long") - } - - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("addedAt"))); err != nil { - return err - } - if _, err := cw.WriteString(string("addedAt")); err != nil { - return err - } - - if t.AddedAt == nil { - if _, err := cw.Write(cbg.CborNull); err != nil { - return err - } - } else { - if len(*t.AddedAt) > 1000000 { - return xerrors.Errorf("Value in field t.AddedAt was too long") - } - - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(*t.AddedAt))); err != nil { - return err - } - if _, err := cw.WriteString(string(*t.AddedAt)); err != nil { - return err - } - } + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.Created))); err != nil { + return err + } + if _, err := cw.WriteString(string(t.Created)); err != nil { + return err } return nil } -func (t *Repo) UnmarshalCBOR(r io.Reader) (err error) { - *t = Repo{} +func (t *PublicKey) UnmarshalCBOR(r io.Reader) (err error) { + *t = PublicKey{} cr := cbg.NewCborReader(r) @@ -761,7 +690,7 @@ } if extra > cbg.MaxLength { - return fmt.Errorf("Repo: map struct too large (%d)", extra) + return fmt.Errorf("PublicKey: map struct too large (%d)", extra) } n := extra @@ -782,8 +711,8 @@ } switch string(nameBuf[:nameLen]) { - // t.Knot (string) (string) - case "knot": + // t.Key (string) (string) + case "key": { sval, err := cbg.ReadStringWithMax(cr, 1000000) @@ -791,7 +720,7 @@ return err } - t.Knot = string(sval) + t.Key = string(sval) } // t.Name (string) (string) case "name": @@ -815,8 +744,8 @@ t.LexiconTypeID = string(sval) } - // t.Owner (string) (string) - case "owner": + // t.Created (string) (string) + case "created": { sval, err := cbg.ReadStringWithMax(cr, 1000000) @@ -824,577 +753,7 @@ return err } - t.Owner = string(sval) - } - // t.AddedAt (string) (string) - case "addedAt": - - { - 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.AddedAt = (*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 *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) - } + t.Created = string(sval) } default: @@ -1825,6 +1184,811 @@ } 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 *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 *Repo) MarshalCBOR(w io.Writer) error { + if t == nil { + _, err := w.Write(cbg.CborNull) + return err + } + + cw := cbg.NewCborWriter(w) + fieldCount := 5 + + if t.AddedAt == nil { + fieldCount-- + } + + if _, err := cw.Write(cbg.CborEncodeMajorType(cbg.MajMap, uint64(fieldCount))); err != nil { + return err + } + + // t.Knot (string) (string) + if len("knot") > 1000000 { + return xerrors.Errorf("Value in field \"knot\" was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("knot"))); err != nil { + return err + } + if _, err := cw.WriteString(string("knot")); err != nil { + return err + } + + if len(t.Knot) > 1000000 { + return xerrors.Errorf("Value in field t.Knot was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.Knot))); err != nil { + return err + } + if _, err := cw.WriteString(string(t.Knot)); err != nil { + return err + } + + // t.Name (string) (string) + if len("name") > 1000000 { + return xerrors.Errorf("Value in field \"name\" was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("name"))); err != nil { + return err + } + if _, err := cw.WriteString(string("name")); err != nil { + return err + } + + if len(t.Name) > 1000000 { + return xerrors.Errorf("Value in field t.Name was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.Name))); err != nil { + return err + } + if _, err := cw.WriteString(string(t.Name)); 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"))); err != nil { + return err + } + if _, err := cw.WriteString(string("sh.tangled.repo")); 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.AddedAt (string) (string) + if t.AddedAt != nil { + + if len("addedAt") > 1000000 { + return xerrors.Errorf("Value in field \"addedAt\" was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("addedAt"))); err != nil { + return err + } + if _, err := cw.WriteString(string("addedAt")); err != nil { + return err + } + + if t.AddedAt == nil { + if _, err := cw.Write(cbg.CborNull); err != nil { + return err + } + } else { + if len(*t.AddedAt) > 1000000 { + return xerrors.Errorf("Value in field t.AddedAt was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(*t.AddedAt))); err != nil { + return err + } + if _, err := cw.WriteString(string(*t.AddedAt)); err != nil { + return err + } + } + } + return nil +} + +func (t *Repo) UnmarshalCBOR(r io.Reader) (err error) { + *t = Repo{} + + 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("Repo: map struct too large (%d)", extra) + } + + n := extra + + nameBuf := make([]byte, 7) + 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.Knot (string) (string) + case "knot": + + { + sval, err := cbg.ReadStringWithMax(cr, 1000000) + if err != nil { + return err + } + + t.Knot = string(sval) + } + // t.Name (string) (string) + case "name": + + { + sval, err := cbg.ReadStringWithMax(cr, 1000000) + if err != nil { + return err + } + + t.Name = 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.AddedAt (string) (string) + case "addedAt": + + { + 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.AddedAt = (*string)(&sval) } } diff --git a/api/tangled/feedstar.go b/api/tangled/feedstar.go new file mode 100644 --- /dev/null +++ b/api/tangled/feedstar.go @@ -0,0 +1,23 @@ +// Code generated by cmd/lexgen (see Makefile's lexgen); DO NOT EDIT. + +package tangled + +// schema: sh.tangled.feed.star + +import ( + "github.com/bluesky-social/indigo/lex/util" +) + +const ( + FeedStarNSID = "sh.tangled.feed.star" +) + +func init() { + util.RegisterType("sh.tangled.feed.star", &FeedStar{}) +} // +// RECORDTYPE: FeedStar +type FeedStar struct { + LexiconTypeID string `json:"$type,const=sh.tangled.feed.star" cborgen:"$type,const=sh.tangled.feed.star"` + CreatedAt string `json:"createdAt" cborgen:"createdAt"` + Subject string `json:"subject" cborgen:"subject"` +} diff --git a/appview/db/db.go b/appview/db/db.go --- a/appview/db/db.go +++ b/appview/db/db.go @@ -112,6 +112,16 @@ next_issue_id integer not null default 1 ); + create table if not exists stars ( + id integer primary key autoincrement, + starred_by_did text not null, + repo_at text not null, + rkey text not null, + created text not null default (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')), + foreign key (repo_at) references repos(at_uri) on delete cascade, + unique(starred_by_did, repo_at) + ); + `) if err != nil { return nil, err diff --git a/appview/db/follow.go b/appview/db/follow.go --- a/appview/db/follow.go +++ b/appview/db/follow.go @@ -9,7 +9,7 @@ UserDid string SubjectDid string FollowedAt time.Time - RKey string + Rkey string } func AddFollow(e Execer, userDid, subjectDid, rkey string) error { @@ -25,7 +25,7 @@ var follow Follow var followedAt string - err := row.Scan(&follow.UserDid, &follow.SubjectDid, &followedAt, &follow.RKey) + err := row.Scan(&follow.UserDid, &follow.SubjectDid, &followedAt, &follow.Rkey) if err != nil { return nil, err } @@ -41,7 +41,7 @@ return &follow, nil } -// Get a follow record +// Remove a follow func DeleteFollow(e Execer, userDid, subjectDid string) error { _, err := e.Exec(`delete from follows where user_did = ? and subject_did = ?`, userDid, subjectDid) return err @@ -91,10 +91,15 @@ } } -func GetAllFollows(e Execer) ([]Follow, error) { +func GetAllFollows(e Execer, limit int) ([]Follow, error) { var follows []Follow - rows, err := e.Query(`select user_did, subject_did, followed_at, rkey from follows`) + rows, err := e.Query(` + select user_did, subject_did, followed_at, rkey + from follows + order by followed_at desc + limit ?`, limit, + ) if err != nil { return nil, err } @@ -103,7 +108,7 @@ for rows.Next() { var follow Follow var followedAt string - if err := rows.Scan(&follow.UserDid, &follow.SubjectDid, &followedAt, &follow.RKey); err != nil { + if err := rows.Scan(&follow.UserDid, &follow.SubjectDid, &followedAt, &follow.Rkey); err != nil { return nil, err } diff --git a/appview/db/issues.go b/appview/db/issues.go --- a/appview/db/issues.go +++ b/appview/db/issues.go @@ -3,10 +3,12 @@ import ( "database/sql" "time" + + "github.com/bluesky-social/indigo/atproto/syntax" ) type Issue struct { - RepoAt string + RepoAt syntax.ATURI OwnerDid string IssueId int IssueAt string @@ -18,7 +20,7 @@ type Comment struct { OwnerDid string - RepoAt string + RepoAt syntax.ATURI CommentAt string Issue int CommentId int @@ -65,30 +67,30 @@ return nil } -func SetIssueAt(e Execer, repoAt string, issueId int, issueAt string) error { +func SetIssueAt(e Execer, repoAt syntax.ATURI, issueId int, issueAt string) error { _, err := e.Exec(`update issues set issue_at = ? where repo_at = ? and issue_id = ?`, issueAt, repoAt, issueId) return err } -func GetIssueAt(e Execer, repoAt string, issueId int) (string, error) { +func GetIssueAt(e Execer, repoAt syntax.ATURI, issueId int) (string, error) { var issueAt string err := e.QueryRow(`select issue_at from issues where repo_at = ? and issue_id = ?`, repoAt, issueId).Scan(&issueAt) return issueAt, err } -func GetIssueId(e Execer, repoAt string) (int, error) { +func GetIssueId(e Execer, repoAt syntax.ATURI) (int, error) { var issueId int err := e.QueryRow(`select next_issue_id from repo_issue_seqs where repo_at = ?`, repoAt).Scan(&issueId) return issueId - 1, err } -func GetIssueOwnerDid(e Execer, repoAt string, issueId int) (string, error) { +func GetIssueOwnerDid(e Execer, repoAt syntax.ATURI, issueId int) (string, error) { var ownerDid string err := e.QueryRow(`select owner_did from issues where repo_at = ? and issue_id = ?`, repoAt, issueId).Scan(&ownerDid) return ownerDid, err } -func GetIssues(e Execer, repoAt string) ([]Issue, error) { +func GetIssues(e Execer, repoAt syntax.ATURI) ([]Issue, error) { var issues []Issue rows, err := e.Query(`select owner_did, issue_id, created, title, body, open from issues where repo_at = ? order by created desc`, repoAt) @@ -121,7 +123,7 @@ return issues, nil } -func GetIssue(e Execer, repoAt string, issueId int) (*Issue, error) { +func GetIssue(e Execer, repoAt syntax.ATURI, issueId int) (*Issue, error) { query := `select owner_did, created, title, body, open from issues where repo_at = ? and issue_id = ?` row := e.QueryRow(query, repoAt, issueId) @@ -141,7 +143,7 @@ return &issue, nil } -func GetIssueWithComments(e Execer, repoAt string, issueId int) (*Issue, []Comment, error) { +func GetIssueWithComments(e Execer, repoAt syntax.ATURI, issueId int) (*Issue, []Comment, error) { query := `select owner_did, issue_id, created, title, body, open from issues where repo_at = ? and issue_id = ?` row := e.QueryRow(query, repoAt, issueId) @@ -180,7 +182,7 @@ return err } -func GetComments(e Execer, repoAt string, issueId int) ([]Comment, error) { +func GetComments(e Execer, repoAt syntax.ATURI, issueId int) ([]Comment, error) { var comments []Comment rows, err := e.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) @@ -216,12 +218,35 @@ return comments, nil } -func CloseIssue(e Execer, repoAt string, issueId int) error { +func CloseIssue(e Execer, repoAt syntax.ATURI, issueId int) error { _, err := e.Exec(`update issues set open = 0 where repo_at = ? and issue_id = ?`, repoAt, issueId) return err } -func ReopenIssue(e Execer, repoAt string, issueId int) error { +func ReopenIssue(e Execer, repoAt syntax.ATURI, issueId int) error { _, err := e.Exec(`update issues set open = 1 where repo_at = ? and issue_id = ?`, repoAt, issueId) return err +} + +type IssueCount struct { + Open int + Closed int +} + +func GetIssueCount(e Execer, repoAt syntax.ATURI) (IssueCount, error) { + row := e.QueryRow(` + select + count(case when open = 1 then 1 end) as open_count, + count(case when open = 0 then 1 end) as closed_count + from issues + where repo_at = ?`, + repoAt, + ) + + var count IssueCount + if err := row.Scan(&count.Open, &count.Closed); err != nil { + return IssueCount{0, 0}, err + } + + return count, nil } diff --git a/appview/db/repos.go b/appview/db/repos.go --- a/appview/db/repos.go +++ b/appview/db/repos.go @@ -14,10 +14,17 @@ AtUri string } -func GetAllRepos(e Execer) ([]Repo, error) { +func GetAllRepos(e Execer, limit int) ([]Repo, error) { var repos []Repo - rows, err := e.Query(`select did, name, knot, rkey, created from repos`) + rows, err := e.Query( + `select did, name, knot, rkey, created + from repos + order by created desc + limit ? + `, + limit, + ) if err != nil { return nil, err } @@ -79,6 +86,21 @@ return &repo, nil } +func GetRepoByAtUri(e Execer, atUri string) (*Repo, error) { + var repo Repo + + row := e.QueryRow(`select did, name, knot, created, at_uri from repos where at_uri = ?`, atUri) + + var createdAt string + if err := row.Scan(&repo.Did, &repo.Name, &repo.Knot, &createdAt, &repo.AtUri); err != nil { + return nil, err + } + createdAtTime, _ := time.Parse(time.RFC3339, createdAt) + repo.Created = createdAtTime + + return &repo, nil +} + func AddRepo(e Execer, repo *Repo) error { _, err := e.Exec(`insert into repos (did, name, knot, rkey, at_uri) values (?, ?, ?, ?, ?)`, repo.Did, repo.Name, repo.Knot, repo.Rkey, repo.AtUri) return err @@ -120,6 +142,11 @@ } return repos, nil +} + +type RepoStats struct { + StarCount int + IssueCount IssueCount } func scanRepo(rows *sql.Rows, did, name, knot, rkey *string, created *time.Time) error { diff --git a/appview/db/star.go b/appview/db/star.go new file mode 100644 --- /dev/null +++ b/appview/db/star.go @@ -0,0 +1,150 @@ +package db + +import ( + "log" + "time" + + "github.com/bluesky-social/indigo/atproto/syntax" +) + +type Star struct { + StarredByDid string + RepoAt syntax.ATURI + Repo *Repo + Created time.Time + Rkey string +} + +func (star *Star) ResolveRepo(e Execer) error { + if star.Repo != nil { + return nil + } + + repo, err := GetRepoByAtUri(e, star.RepoAt.String()) + if err != nil { + return err + } + + star.Repo = repo + return nil +} + +func AddStar(e Execer, starredByDid string, repoAt syntax.ATURI, rkey string) error { + query := `insert or ignore into stars (starred_by_did, repo_at, rkey) values (?, ?, ?)` + _, err := e.Exec(query, starredByDid, repoAt, rkey) + return err +} + +// Get a star record +func GetStar(e Execer, starredByDid string, repoAt syntax.ATURI) (*Star, error) { + query := ` + select starred_by_did, repo_at, created, rkey + from stars + where starred_by_did = ? and repo_at = ?` + row := e.QueryRow(query, starredByDid, repoAt) + + var star Star + var created string + err := row.Scan(&star.StarredByDid, &star.RepoAt, &created, &star.Rkey) + if err != nil { + return nil, err + } + + createdAtTime, err := time.Parse(time.RFC3339, created) + if err != nil { + log.Println("unable to determine followed at time") + star.Created = time.Now() + } else { + star.Created = createdAtTime + } + + return &star, nil +} + +// Remove a star +func DeleteStar(e Execer, starredByDid string, repoAt syntax.ATURI) error { + _, err := e.Exec(`delete from stars where starred_by_did = ? and repo_at = ?`, starredByDid, repoAt) + return err +} + +func GetStarCount(e Execer, repoAt syntax.ATURI) (int, error) { + stars := 0 + err := e.QueryRow( + `select count(starred_by_did) from stars where repo_at = ?`, repoAt).Scan(&stars) + if err != nil { + return 0, err + } + return stars, nil +} + +func GetStarStatus(e Execer, userDid string, repoAt syntax.ATURI) bool { + if _, err := GetStar(e, userDid, repoAt); err != nil { + return false + } else { + return true + } +} + +func GetAllStars(e Execer, limit int) ([]Star, error) { + var stars []Star + + rows, err := e.Query(` + select + s.starred_by_did, + s.repo_at, + s.rkey, + s.created, + r.did, + r.name, + r.knot, + r.rkey, + r.created, + r.at_uri + from stars s + join repos r on s.repo_at = r.at_uri + `) + + if err != nil { + return nil, err + } + defer rows.Close() + + for rows.Next() { + var star Star + var repo Repo + var starCreatedAt, repoCreatedAt string + + if err := rows.Scan( + &star.StarredByDid, + &star.RepoAt, + &star.Rkey, + &starCreatedAt, + &repo.Did, + &repo.Name, + &repo.Knot, + &repo.Rkey, + &repoCreatedAt, + &repo.AtUri, + ); err != nil { + return nil, err + } + + star.Created, err = time.Parse(time.RFC3339, starCreatedAt) + if err != nil { + star.Created = time.Now() + } + repo.Created, err = time.Parse(time.RFC3339, repoCreatedAt) + if err != nil { + repo.Created = time.Now() + } + star.Repo = &repo + + stars = append(stars, star) + } + + if err := rows.Err(); err != nil { + return nil, err + } + + return stars, nil +} diff --git a/appview/db/timeline.go b/appview/db/timeline.go --- a/appview/db/timeline.go +++ b/appview/db/timeline.go @@ -8,18 +8,27 @@ type TimelineEvent struct { *Repo *Follow + *Star EventAt time.Time } +// TODO: this gathers heterogenous events from different sources and aggregates +// them in code; if we did this entirely in sql, we could order and limit and paginate easily func MakeTimeline(e Execer) ([]TimelineEvent, error) { var events []TimelineEvent + limit := 50 - repos, err := GetAllRepos(e) + repos, err := GetAllRepos(e, limit) if err != nil { return nil, err } - follows, err := GetAllFollows(e) + follows, err := GetAllFollows(e, limit) + if err != nil { + return nil, err + } + + stars, err := GetAllStars(e, limit) if err != nil { return nil, err } @@ -27,16 +36,21 @@ for _, repo := range repos { events = append(events, TimelineEvent{ Repo: &repo, - Follow: nil, EventAt: repo.Created, }) } for _, follow := range follows { events = append(events, TimelineEvent{ - Repo: nil, Follow: &follow, EventAt: follow.FollowedAt, + }) + } + + for _, star := range stars { + events = append(events, TimelineEvent{ + Star: &star, + EventAt: star.Created, }) } @@ -45,8 +59,8 @@ }) // Limit the slice to 100 events - if len(events) > 50 { - events = events[:50] + if len(events) > limit { + events = events[:limit] } return events, nil diff --git a/appview/pages/funcmap.go b/appview/pages/funcmap.go --- a/appview/pages/funcmap.go +++ b/appview/pages/funcmap.go @@ -106,5 +106,12 @@ "markdown": func(text string) template.HTML { return template.HTML(renderMarkdown(text)) }, + "isNil": func(t any) bool { + // returns false for other "zero" values + return t == nil + }, + "not": func(t bool) bool { + return !t + }, } } diff --git a/appview/pages/pages.go b/appview/pages/pages.go --- a/appview/pages/pages.go +++ b/appview/pages/pages.go @@ -17,6 +17,7 @@ chromahtml "github.com/alecthomas/chroma/v2/formatters/html" "github.com/alecthomas/chroma/v2/lexers" "github.com/alecthomas/chroma/v2/styles" + "github.com/bluesky-social/indigo/atproto/syntax" "github.com/microcosm-cc/bluemonday" "github.com/sotangled/tangled/appview/auth" "github.com/sotangled/tangled/appview/db" @@ -43,11 +44,26 @@ name := strings.TrimPrefix(path, "templates/") name = strings.TrimSuffix(name, ".html") - if !strings.HasPrefix(path, "templates/layouts/") { + // add fragments as templates + if strings.HasPrefix(path, "templates/fragments/") { + tmpl, err := template.New(name). + Funcs(funcMap()). + ParseFS(files, path) + if err != nil { + return fmt.Errorf("setting up fragment: %w", err) + } + + templates[name] = tmpl + log.Printf("loaded fragment: %s", name) + } + + // layouts and fragments are applied first + if !strings.HasPrefix(path, "templates/layouts/") && + !strings.HasPrefix(path, "templates/fragments/") { // Add the page template on top of the base tmpl, err := template.New(name). Funcs(funcMap()). - ParseFS(files, "templates/layouts/*.html", path) + ParseFS(files, "templates/layouts/*.html", "templates/fragments/*.html", path) if err != nil { return fmt.Errorf("setting up template: %w", err) } @@ -159,13 +175,35 @@ return p.execute("user/profile", w, params) } +type FollowFragmentParams struct { + UserDid string + FollowStatus db.FollowStatus +} + +func (p *Pages) FollowFragment(w io.Writer, params FollowFragmentParams) error { + return p.executePlain("fragments/follow", w, params) +} + +type StarFragmentParams struct { + IsStarred bool + RepoAt syntax.ATURI + Stats db.RepoStats +} + +func (p *Pages) StarFragment(w io.Writer, params StarFragmentParams) error { + return p.executePlain("fragments/star", w, params) +} + type RepoInfo struct { Name string OwnerDid string OwnerHandle string Description string Knot string + RepoAt syntax.ATURI SettingsAllowed bool + IsStarred bool + Stats db.RepoStats } func (r RepoInfo) OwnerWithAt() string { @@ -192,6 +230,23 @@ } return tabs +} + +// each tab on a repo could have some metadata: +// +// issues -> number of open issues etc. +// settings -> a warning icon to setup branch protection? idk +// +// we gather these bits of info here, because go templates +// are difficult to program in +func (r RepoInfo) TabMetadata() map[string]any { + meta := make(map[string]any) + + meta["issues"] = r.Stats.IssueCount.Open + + // more stuff? + + return meta } type RepoIndexParams struct { diff --git a/appview/state/follow.go b/appview/state/follow.go --- a/appview/state/follow.go +++ b/appview/state/follow.go @@ -1,7 +1,6 @@ package state import ( - "fmt" "log" "net/http" "time" @@ -10,6 +9,7 @@ lexutil "github.com/bluesky-social/indigo/lex/util" tangled "github.com/sotangled/tangled/api/tangled" "github.com/sotangled/tangled/appview/db" + "github.com/sotangled/tangled/appview/pages" ) func (s *State) Follow(w http.ResponseWriter, r *http.Request) { @@ -60,16 +60,10 @@ log.Println("created atproto record: ", resp.Uri) - w.Write([]byte(fmt.Sprintf(` - - `, subjectIdent.DID.String()))) + s.pages.FollowFragment(w, pages.FollowFragmentParams{ + UserDid: subjectIdent.DID.String(), + FollowStatus: db.IsFollowing, + }) return case http.MethodDelete: @@ -83,7 +77,7 @@ _, err = comatproto.RepoDeleteRecord(r.Context(), client, &comatproto.RepoDeleteRecord_Input{ Collection: tangled.GraphFollowNSID, Repo: currentUser.Did, - Rkey: follow.RKey, + Rkey: follow.Rkey, }) if err != nil { @@ -97,16 +91,11 @@ // this is not an issue, the firehose event might have already done this } - w.Write([]byte(fmt.Sprintf(` - - `, subjectIdent.DID.String()))) + s.pages.FollowFragment(w, pages.FollowFragmentParams{ + UserDid: subjectIdent.DID.String(), + FollowStatus: db.IsNotFollowing, + }) + return } diff --git a/appview/state/jetstream.go b/appview/state/jetstream.go --- a/appview/state/jetstream.go +++ b/appview/state/jetstream.go @@ -6,6 +6,7 @@ "fmt" "log" + "github.com/bluesky-social/indigo/atproto/syntax" "github.com/bluesky-social/jetstream/pkg/models" tangled "github.com/sotangled/tangled/api/tangled" "github.com/sotangled/tangled/appview/db" @@ -40,6 +41,25 @@ return err } err = db.AddFollow(d, did, record.Subject, e.Commit.RKey) + if err != nil { + return fmt.Errorf("failed to add follow to db: %w", err) + } + case tangled.FeedStarNSID: + record := tangled.FeedStar{} + err := json.Unmarshal(raw, &record) + if err != nil { + log.Println("invalid record") + return err + } + + subjectUri, err := syntax.ParseATURI(record.Subject) + + if err != nil { + log.Println("invalid record") + return err + } + + err = db.AddStar(d, did, subjectUri, e.Commit.RKey) if err != nil { return fmt.Errorf("failed to add follow to db: %w", err) } diff --git a/appview/state/repo.go b/appview/state/repo.go --- a/appview/state/repo.go +++ b/appview/state/repo.go @@ -15,6 +15,7 @@ "time" "github.com/bluesky-social/indigo/atproto/identity" + "github.com/bluesky-social/indigo/atproto/syntax" securejoin "github.com/cyphar/filepath-securejoin" "github.com/go-chi/chi/v5" "github.com/sotangled/tangled/api/tangled" @@ -75,20 +76,9 @@ user := s.auth.GetUser(r) - knot := f.Knot - if knot == "knot1.tangled.sh" { - knot = "tangled.sh" - } - s.pages.RepoIndexPage(w, pages.RepoIndexParams{ - LoggedInUser: user, - RepoInfo: pages.RepoInfo{ - OwnerDid: f.OwnerDid(), - OwnerHandle: f.OwnerHandle(), - Name: f.RepoName, - Knot: knot, - SettingsAllowed: settingsAllowed(s, user, f), - }, + LoggedInUser: user, + RepoInfo: f.RepoInfo(s, user), TagMap: tagMap, RepoIndexResponse: result, }) @@ -133,13 +123,8 @@ user := s.auth.GetUser(r) s.pages.RepoLog(w, pages.RepoLogParams{ - LoggedInUser: user, - RepoInfo: pages.RepoInfo{ - OwnerDid: f.OwnerDid(), - OwnerHandle: f.OwnerHandle(), - Name: f.RepoName, - SettingsAllowed: settingsAllowed(s, user, f), - }, + LoggedInUser: user, + RepoInfo: f.RepoInfo(s, user), RepoLogResponse: repolog, }) return @@ -174,13 +159,8 @@ user := s.auth.GetUser(r) s.pages.RepoCommit(w, pages.RepoCommitParams{ - LoggedInUser: user, - RepoInfo: pages.RepoInfo{ - OwnerDid: f.OwnerDid(), - OwnerHandle: f.OwnerHandle(), - Name: f.RepoName, - SettingsAllowed: settingsAllowed(s, user, f), - }, + LoggedInUser: user, + RepoInfo: f.RepoInfo(s, user), RepoCommitResponse: result, }) return @@ -228,16 +208,11 @@ baseBlobLink := path.Join(f.OwnerDid(), f.RepoName, "blob", ref, treePath) s.pages.RepoTree(w, pages.RepoTreeParams{ - LoggedInUser: user, - BreadCrumbs: breadcrumbs, - BaseTreeLink: baseTreeLink, - BaseBlobLink: baseBlobLink, - RepoInfo: pages.RepoInfo{ - OwnerDid: f.OwnerDid(), - OwnerHandle: f.OwnerHandle(), - Name: f.RepoName, - SettingsAllowed: settingsAllowed(s, user, f), - }, + LoggedInUser: user, + BreadCrumbs: breadcrumbs, + BaseTreeLink: baseTreeLink, + BaseBlobLink: baseBlobLink, + RepoInfo: f.RepoInfo(s, user), RepoTreeResponse: result, }) return @@ -271,13 +246,8 @@ user := s.auth.GetUser(r) s.pages.RepoTags(w, pages.RepoTagsParams{ - LoggedInUser: user, - RepoInfo: pages.RepoInfo{ - OwnerDid: f.OwnerDid(), - OwnerHandle: f.OwnerHandle(), - Name: f.RepoName, - SettingsAllowed: settingsAllowed(s, user, f), - }, + LoggedInUser: user, + RepoInfo: f.RepoInfo(s, user), RepoTagsResponse: result, }) return @@ -311,13 +281,8 @@ user := s.auth.GetUser(r) s.pages.RepoBranches(w, pages.RepoBranchesParams{ - LoggedInUser: user, - RepoInfo: pages.RepoInfo{ - OwnerDid: f.OwnerDid(), - OwnerHandle: f.OwnerHandle(), - Name: f.RepoName, - SettingsAllowed: settingsAllowed(s, user, f), - }, + LoggedInUser: user, + RepoInfo: f.RepoInfo(s, user), RepoBranchesResponse: result, }) return @@ -361,13 +326,8 @@ user := s.auth.GetUser(r) s.pages.RepoBlob(w, pages.RepoBlobParams{ - LoggedInUser: user, - RepoInfo: pages.RepoInfo{ - OwnerDid: f.OwnerDid(), - OwnerHandle: f.OwnerHandle(), - Name: f.RepoName, - SettingsAllowed: settingsAllowed(s, user, f), - }, + LoggedInUser: user, + RepoInfo: f.RepoInfo(s, user), RepoBlobResponse: result, BreadCrumbs: breadcrumbs, }) @@ -488,13 +448,8 @@ } s.pages.RepoSettings(w, pages.RepoSettingsParams{ - LoggedInUser: user, - RepoInfo: pages.RepoInfo{ - OwnerDid: f.OwnerDid(), - OwnerHandle: f.OwnerHandle(), - Name: f.RepoName, - SettingsAllowed: settingsAllowed(s, user, f), - }, + LoggedInUser: user, + RepoInfo: f.RepoInfo(s, user), Collaborators: repoCollaborators, IsCollaboratorInviteAllowed: isCollaboratorInviteAllowed, }) @@ -505,7 +460,7 @@ Knot string OwnerId identity.Identity RepoName string - RepoAt string + RepoAt syntax.ATURI } func (f *FullyResolvedRepo) OwnerDid() string { @@ -565,6 +520,41 @@ return collaborators, nil } +func (f *FullyResolvedRepo) RepoInfo(s *State, u *auth.User) pages.RepoInfo { + isStarred := false + if u != nil { + isStarred = db.GetStarStatus(s.db, u.Did, syntax.ATURI(f.RepoAt)) + } + + starCount, err := db.GetStarCount(s.db, f.RepoAt) + if err != nil { + log.Println("failed to get star count for ", f.RepoAt) + } + issueCount, err := db.GetIssueCount(s.db, f.RepoAt) + if err != nil { + log.Println("failed to get issue count for ", f.RepoAt) + } + + knot := f.Knot + if knot == "knot1.tangled.sh" { + knot = "tangled.sh" + } + + return pages.RepoInfo{ + OwnerDid: f.OwnerDid(), + OwnerHandle: f.OwnerHandle(), + Name: f.RepoName, + RepoAt: f.RepoAt, + SettingsAllowed: settingsAllowed(s, u, f), + IsStarred: isStarred, + Knot: knot, + Stats: db.RepoStats{ + StarCount: starCount, + IssueCount: issueCount, + }, + } +} + func (s *State) RepoSingleIssue(w http.ResponseWriter, r *http.Request) { user := s.auth.GetUser(r) f, err := fullyResolvedRepo(r) @@ -609,14 +599,9 @@ s.pages.RepoSingleIssue(w, pages.RepoSingleIssueParams{ LoggedInUser: user, - RepoInfo: pages.RepoInfo{ - OwnerDid: f.OwnerDid(), - OwnerHandle: f.OwnerHandle(), - Name: f.RepoName, - SettingsAllowed: settingsAllowed(s, user, f), - }, - Issue: *issue, - Comments: comments, + RepoInfo: f.RepoInfo(s, user), + Issue: *issue, + Comments: comments, IssueOwnerHandle: issueOwnerIdent.Handle.String(), DidHandleMap: didHandleMap, @@ -777,6 +762,7 @@ return } + atUri := f.RepoAt.String() client, _ := s.auth.AuthorizedClient(r) _, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ Collection: tangled.RepoIssueCommentNSID, @@ -784,7 +770,7 @@ Rkey: s.TID(), Record: &lexutil.LexiconTypeDecoder{ Val: &tangled.RepoIssueComment{ - Repo: &f.RepoAt, + Repo: &atUri, Issue: issueAt, CommentId: &commentIdInt64, Owner: &ownerDid, @@ -835,12 +821,7 @@ s.pages.RepoIssues(w, pages.RepoIssuesParams{ LoggedInUser: s.auth.GetUser(r), - RepoInfo: pages.RepoInfo{ - OwnerDid: f.OwnerDid(), - OwnerHandle: f.OwnerHandle(), - Name: f.RepoName, - SettingsAllowed: settingsAllowed(s, user, f), - }, + RepoInfo: f.RepoInfo(s, user), Issues: issues, DidHandleMap: didHandleMap, }) @@ -860,12 +841,7 @@ case http.MethodGet: s.pages.RepoNewIssue(w, pages.RepoNewIssueParams{ LoggedInUser: user, - RepoInfo: pages.RepoInfo{ - Name: f.RepoName, - OwnerDid: f.OwnerDid(), - OwnerHandle: f.OwnerHandle(), - SettingsAllowed: settingsAllowed(s, user, f), - }, + RepoInfo: f.RepoInfo(s, user), }) case http.MethodPost: title := r.FormValue("title") @@ -902,13 +878,14 @@ } client, _ := s.auth.AuthorizedClient(r) + atUri := f.RepoAt.String() 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, + Repo: atUri, Title: title, Body: &body, Owner: user.Did, @@ -946,12 +923,7 @@ case http.MethodGet: s.pages.RepoPulls(w, pages.RepoPullsParams{ LoggedInUser: user, - RepoInfo: pages.RepoInfo{ - Name: f.RepoName, - OwnerDid: f.OwnerDid(), - OwnerHandle: f.OwnerHandle(), - SettingsAllowed: settingsAllowed(s, user, f), - }, + RepoInfo: f.RepoInfo(s, user), }) } } @@ -975,11 +947,17 @@ return nil, fmt.Errorf("malformed middleware") } + parsedRepoAt, err := syntax.ParseATURI(repoAt) + if err != nil { + log.Println("malformed repo at-uri") + return nil, fmt.Errorf("malformed middleware") + } + return &FullyResolvedRepo{ Knot: knot, OwnerId: id, RepoName: repoName, - RepoAt: repoAt, + RepoAt: parsedRepoAt, }, nil } diff --git a/appview/state/star.go b/appview/state/star.go new file mode 100644 --- /dev/null +++ b/appview/state/star.go @@ -0,0 +1,115 @@ +package state + +import ( + "log" + "net/http" + "time" + + comatproto "github.com/bluesky-social/indigo/api/atproto" + "github.com/bluesky-social/indigo/atproto/syntax" + lexutil "github.com/bluesky-social/indigo/lex/util" + tangled "github.com/sotangled/tangled/api/tangled" + "github.com/sotangled/tangled/appview/db" + "github.com/sotangled/tangled/appview/pages" +) + +func (s *State) Star(w http.ResponseWriter, r *http.Request) { + currentUser := s.auth.GetUser(r) + + subject := r.URL.Query().Get("subject") + if subject == "" { + log.Println("invalid form") + return + } + + subjectUri, err := syntax.ParseATURI(subject) + if err != nil { + log.Println("invalid form") + return + } + + client, _ := s.auth.AuthorizedClient(r) + + switch r.Method { + case http.MethodPost: + createdAt := time.Now().Format(time.RFC3339) + rkey := s.TID() + resp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ + Collection: tangled.FeedStarNSID, + Repo: currentUser.Did, + Rkey: rkey, + Record: &lexutil.LexiconTypeDecoder{ + Val: &tangled.FeedStar{ + Subject: subjectUri.String(), + CreatedAt: createdAt, + }}, + }) + if err != nil { + log.Println("failed to create atproto record", err) + return + } + + err = db.AddStar(s.db, currentUser.Did, subjectUri, rkey) + if err != nil { + log.Println("failed to star", err) + return + } + + starCount, err := db.GetStarCount(s.db, subjectUri) + if err != nil { + log.Println("failed to get star count for ", subjectUri) + } + + log.Println("created atproto record: ", resp.Uri) + + s.pages.StarFragment(w, pages.StarFragmentParams{ + IsStarred: true, + RepoAt: subjectUri, + Stats: db.RepoStats{ + StarCount: starCount, + }, + }) + + return + case http.MethodDelete: + // find the record in the db + star, err := db.GetStar(s.db, currentUser.Did, subjectUri) + if err != nil { + log.Println("failed to get star relationship") + return + } + + _, err = comatproto.RepoDeleteRecord(r.Context(), client, &comatproto.RepoDeleteRecord_Input{ + Collection: tangled.FeedStarNSID, + Repo: currentUser.Did, + Rkey: star.Rkey, + }) + + if err != nil { + log.Println("failed to unstar") + return + } + + err = db.DeleteStar(s.db, currentUser.Did, subjectUri) + if err != nil { + log.Println("failed to delete star from DB") + // this is not an issue, the firehose event might have already done this + } + + starCount, err := db.GetStarCount(s.db, subjectUri) + if err != nil { + log.Println("failed to get star count for ", subjectUri) + } + + s.pages.StarFragment(w, pages.StarFragmentParams{ + IsStarred: false, + RepoAt: subjectUri, + Stats: db.RepoStats{ + StarCount: starCount, + }, + }) + + return + } + +} diff --git a/appview/state/state.go b/appview/state/state.go --- a/appview/state/state.go +++ b/appview/state/state.go @@ -184,8 +184,10 @@ didsToResolve = append(didsToResolve, ev.Repo.Did) } if ev.Follow != nil { - didsToResolve = append(didsToResolve, ev.Follow.UserDid) - didsToResolve = append(didsToResolve, ev.Follow.SubjectDid) + didsToResolve = append(didsToResolve, ev.Follow.UserDid, ev.Follow.SubjectDid) + } + if ev.Star != nil { + didsToResolve = append(didsToResolve, ev.Star.StarredByDid, ev.Star.Repo.Did) } } @@ -931,6 +933,11 @@ r.With(AuthMiddleware(s)).Route("/follow", func(r chi.Router) { r.Post("/", s.Follow) r.Delete("/", s.Follow) + }) + + r.With(AuthMiddleware(s)).Route("/star", func(r chi.Router) { + r.Post("/", s.Star) + r.Delete("/", s.Star) }) r.Route("/settings", func(r chi.Router) { 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 @@ -40,29 +40,41 @@ {{ range .Timeline }}
- {{ $userHandle }} - created - {{ .Repo.Name }} - -
-+ {{ $userHandle }} + created + {{ .Repo.Name }} + +
+- {{ $userHandle }} - followed - {{ $subjectHandle }} - -
-+ {{ $userHandle }} + followed + {{ $subjectHandle }} + +
++ {{ $starrerHandle }} + starred + {{ $repoOwnerHandle }}/{{ .Star.Repo.Name }} + +
+- {{ .RepoInfo.OwnerWithAt }} - / - {{ .RepoInfo.Name }} + {{ .RepoInfo.OwnerWithAt }} + / + {{ .RepoInfo.Name }}
- - {{ if .RepoInfo.Description }} - {{ .RepoInfo.Description }} - {{ else }} - this repo has no description - {{ end }} - + {{ template "fragments/star" .RepoInfo }} +