diff --git a/api/tangled/cbor_gen.go b/api/tangled/cbor_gen.go index 3ff2b26b..907d640d 100644 --- a/api/tangled/cbor_gen.go +++ b/api/tangled/cbor_gen.go @@ -4225,6 +4225,513 @@ func (t *FeedStar_String) UnmarshalCBOR(r io.Reader) (err error) { return nil } +func (t *FeedSubscription) MarshalCBOR(w io.Writer) error { + if t == nil { + _, err := w.Write(cbg.CborNull) + return err + } + + cw := cbg.NewCborWriter(w) + fieldCount := 4 + + if t.Collections == 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.feed.subscription"))); err != nil { + return err + } + if _, err := cw.WriteString(string("sh.tangled.feed.subscription")); err != nil { + return err + } + + // t.Subject (tangled.FeedSubscription_Subject) (struct) + 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 err := t.Subject.MarshalCBOR(cw); 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 + } + + // t.Collections ([]string) (slice) + if t.Collections != nil { + + if len("collections") > 1000000 { + return xerrors.Errorf("Value in field \"collections\" was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("collections"))); err != nil { + return err + } + if _, err := cw.WriteString(string("collections")); err != nil { + return err + } + + if len(t.Collections) > 8192 { + return xerrors.Errorf("Slice value in field t.Collections was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajArray, uint64(len(t.Collections))); err != nil { + return err + } + for _, v := range t.Collections { + if len(v) > 1000000 { + return xerrors.Errorf("Value in field v was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(v))); err != nil { + return err + } + if _, err := cw.WriteString(string(v)); err != nil { + return err + } + + } + } + return nil +} + +func (t *FeedSubscription) UnmarshalCBOR(r io.Reader) (err error) { + *t = FeedSubscription{} + + 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("FeedSubscription: map struct too large (%d)", extra) + } + + n := extra + + nameBuf := make([]byte, 11) + 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 (tangled.FeedSubscription_Subject) (struct) + case "subject": + + { + + b, err := cr.ReadByte() + if err != nil { + return err + } + if b != cbg.CborNull[0] { + if err := cr.UnreadByte(); err != nil { + return err + } + t.Subject = new(FeedSubscription_Subject) + if err := t.Subject.UnmarshalCBOR(cr); err != nil { + return xerrors.Errorf("unmarshaling t.Subject pointer: %w", err) + } + } + + } + // t.CreatedAt (string) (string) + case "createdAt": + + { + sval, err := cbg.ReadStringWithMax(cr, 1000000) + if err != nil { + return err + } + + t.CreatedAt = string(sval) + } + // t.Collections ([]string) (slice) + case "collections": + + maj, extra, err = cr.ReadHeader() + if err != nil { + return err + } + + if extra > 8192 { + return fmt.Errorf("t.Collections: array too large (%d)", extra) + } + + if maj != cbg.MajArray { + return fmt.Errorf("expected cbor array") + } + + if extra > 0 { + t.Collections = make([]string, extra) + } + + for i := 0; i < int(extra); i++ { + { + var maj byte + var extra uint64 + var err error + _ = maj + _ = extra + _ = err + + { + sval, err := cbg.ReadStringWithMax(cr, 1000000) + if err != nil { + return err + } + + t.Collections[i] = 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 *FeedSubscription_Uri) MarshalCBOR(w io.Writer) error { + if t == nil { + _, err := w.Write(cbg.CborNull) + return err + } + + cw := cbg.NewCborWriter(w) + + if _, err := cw.Write([]byte{162}); err != nil { + return err + } + + // t.Uri (string) (string) + if len("uri") > 1000000 { + return xerrors.Errorf("Value in field \"uri\" was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("uri"))); err != nil { + return err + } + if _, err := cw.WriteString(string("uri")); err != nil { + return err + } + + if len(t.Uri) > 1000000 { + return xerrors.Errorf("Value in field t.Uri was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.Uri))); err != nil { + return err + } + if _, err := cw.WriteString(string(t.Uri)); 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.feed.subscription#uri"))); err != nil { + return err + } + if _, err := cw.WriteString(string("sh.tangled.feed.subscription#uri")); err != nil { + return err + } + return nil +} + +func (t *FeedSubscription_Uri) UnmarshalCBOR(r io.Reader) (err error) { + *t = FeedSubscription_Uri{} + + 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("FeedSubscription_Uri: 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.Uri (string) (string) + case "uri": + + { + sval, err := cbg.ReadStringWithMax(cr, 1000000) + if err != nil { + return err + } + + t.Uri = string(sval) + } + // t.LexiconTypeID (string) (string) + case "$type": + + { + sval, err := cbg.ReadStringWithMax(cr, 1000000) + if err != nil { + return err + } + + t.LexiconTypeID = 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 *FeedSubscription_Repo) MarshalCBOR(w io.Writer) error { + if t == nil { + _, err := w.Write(cbg.CborNull) + return err + } + + cw := cbg.NewCborWriter(w) + + if _, err := cw.Write([]byte{162}); err != nil { + return err + } + + // t.Did (string) (string) + if len("did") > 1000000 { + return xerrors.Errorf("Value in field \"did\" was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("did"))); err != nil { + return err + } + if _, err := cw.WriteString(string("did")); err != nil { + return err + } + + if len(t.Did) > 1000000 { + return xerrors.Errorf("Value in field t.Did was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.Did))); err != nil { + return err + } + if _, err := cw.WriteString(string(t.Did)); 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.feed.subscription#repo"))); err != nil { + return err + } + if _, err := cw.WriteString(string("sh.tangled.feed.subscription#repo")); err != nil { + return err + } + return nil +} + +func (t *FeedSubscription_Repo) UnmarshalCBOR(r io.Reader) (err error) { + *t = FeedSubscription_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("FeedSubscription_Repo: 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.Did (string) (string) + case "did": + + { + sval, err := cbg.ReadStringWithMax(cr, 1000000) + if err != nil { + return err + } + + t.Did = string(sval) + } + // t.LexiconTypeID (string) (string) + case "$type": + + { + sval, err := cbg.ReadStringWithMax(cr, 1000000) + if err != nil { + return err + } + + t.LexiconTypeID = 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 *GitRefUpdate) MarshalCBOR(w io.Writer) error { if t == nil { _, err := w.Write(cbg.CborNull) diff --git a/api/tangled/feedsubscription.go b/api/tangled/feedsubscription.go new file mode 100644 index 00000000..99f963d9 --- /dev/null +++ b/api/tangled/feedsubscription.go @@ -0,0 +1,115 @@ +// Code generated by cmd/lexgen (see Makefile's lexgen); DO NOT EDIT. + +package tangled + +// schema: sh.tangled.feed.subscription + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + + "github.com/bluesky-social/indigo/lex/util" + cbg "github.com/whyrusleeping/cbor-gen" +) + +const ( + FeedSubscriptionNSID = "sh.tangled.feed.subscription" +) + +func init() { + util.RegisterType("sh.tangled.feed.subscription", &FeedSubscription{}) +} // +// RECORDTYPE: FeedSubscription +type FeedSubscription struct { + LexiconTypeID string `json:"$type,const=sh.tangled.feed.subscription" cborgen:"$type,const=sh.tangled.feed.subscription"` + // collections: Optional collection NSIDs to filter which notifications are sent. Empty or absent means all collections. + Collections []string `json:"collections,omitempty" cborgen:"collections,omitempty"` + CreatedAt string `json:"createdAt" cborgen:"createdAt"` + Subject *FeedSubscription_Subject `json:"subject" cborgen:"subject"` +} + +// FeedSubscription_Repo is a "repo" in the sh.tangled.feed.subscription schema. +// +// RECORDTYPE: FeedSubscription_Repo +type FeedSubscription_Repo struct { + LexiconTypeID string `json:"$type,const=sh.tangled.feed.subscription#repo" cborgen:"$type,const=sh.tangled.feed.subscription#repo"` + Did string `json:"did" cborgen:"did"` +} + +type FeedSubscription_Subject struct { + FeedSubscription_Uri *FeedSubscription_Uri + FeedSubscription_Repo *FeedSubscription_Repo +} + +func (t *FeedSubscription_Subject) MarshalJSON() ([]byte, error) { + if t.FeedSubscription_Uri != nil { + t.FeedSubscription_Uri.LexiconTypeID = "sh.tangled.feed.subscription#uri" + return json.Marshal(t.FeedSubscription_Uri) + } + if t.FeedSubscription_Repo != nil { + t.FeedSubscription_Repo.LexiconTypeID = "sh.tangled.feed.subscription#repo" + return json.Marshal(t.FeedSubscription_Repo) + } + return nil, fmt.Errorf("cannot marshal empty enum") +} +func (t *FeedSubscription_Subject) UnmarshalJSON(b []byte) error { + typ, err := util.TypeExtract(b) + if err != nil { + return err + } + + switch typ { + case "sh.tangled.feed.subscription#uri": + t.FeedSubscription_Uri = new(FeedSubscription_Uri) + return json.Unmarshal(b, t.FeedSubscription_Uri) + case "sh.tangled.feed.subscription#repo": + t.FeedSubscription_Repo = new(FeedSubscription_Repo) + return json.Unmarshal(b, t.FeedSubscription_Repo) + + default: + return fmt.Errorf("closed enums must have a matching value") + } +} + +func (t *FeedSubscription_Subject) MarshalCBOR(w io.Writer) error { + + if t == nil { + _, err := w.Write(cbg.CborNull) + return err + } + if t.FeedSubscription_Uri != nil { + return t.FeedSubscription_Uri.MarshalCBOR(w) + } + if t.FeedSubscription_Repo != nil { + return t.FeedSubscription_Repo.MarshalCBOR(w) + } + return fmt.Errorf("cannot cbor marshal empty enum") +} +func (t *FeedSubscription_Subject) UnmarshalCBOR(r io.Reader) error { + typ, b, err := util.CborTypeExtractReader(r) + if err != nil { + return err + } + + switch typ { + case "sh.tangled.feed.subscription#uri": + t.FeedSubscription_Uri = new(FeedSubscription_Uri) + return t.FeedSubscription_Uri.UnmarshalCBOR(bytes.NewReader(b)) + case "sh.tangled.feed.subscription#repo": + t.FeedSubscription_Repo = new(FeedSubscription_Repo) + return t.FeedSubscription_Repo.UnmarshalCBOR(bytes.NewReader(b)) + + default: + return fmt.Errorf("closed enums must have a matching value") + } +} + +// FeedSubscription_Uri is a "uri" in the sh.tangled.feed.subscription schema. +// +// RECORDTYPE: FeedSubscription_Uri +type FeedSubscription_Uri struct { + LexiconTypeID string `json:"$type,const=sh.tangled.feed.subscription#uri" cborgen:"$type,const=sh.tangled.feed.subscription#uri"` + Uri string `json:"uri" cborgen:"uri"` +} diff --git a/bobbin/crates/types/src/edges.rs b/bobbin/crates/types/src/edges.rs index ea9d926b..757aa5e2 100644 --- a/bobbin/crates/types/src/edges.rs +++ b/bobbin/crates/types/src/edges.rs @@ -12,6 +12,7 @@ use crate::sh_tangled::actor::profile::Profile; use crate::sh_tangled::feed::comment::Comment as FeedCommentRecord; use crate::sh_tangled::feed::reaction::Reaction; use crate::sh_tangled::feed::star::Star; +use crate::sh_tangled::feed::subscription::Subscription; use crate::sh_tangled::git::ref_update::RefUpdate; use crate::sh_tangled::graph::follow::Follow; use crate::sh_tangled::graph::vouch::Vouch; @@ -57,6 +58,7 @@ pub enum Record { FeedComment(FeedCommentRecord), Reaction(Reaction), Star(Star), + Subscription(Subscription), RefUpdate(RefUpdate), Follow(Follow), Vouch(Vouch), @@ -102,6 +104,7 @@ impl Record { "sh.tangled.feed.comment" => parse!(FeedComment), "sh.tangled.feed.reaction" => parse!(Reaction), "sh.tangled.feed.star" => parse!(Star), + "sh.tangled.feed.subscription" => parse!(Subscription), "sh.tangled.git.refUpdate" => parse!(RefUpdate), "sh.tangled.graph.follow" => parse!(Follow), "sh.tangled.graph.vouch" => parse!(Vouch), @@ -132,6 +135,7 @@ impl Record { Self::FeedComment(_) => "sh.tangled.feed.comment", Self::Reaction(_) => "sh.tangled.feed.reaction", Self::Star(_) => "sh.tangled.feed.star", + Self::Subscription(_) => "sh.tangled.feed.subscription", Self::RefUpdate(_) => "sh.tangled.git.refUpdate", Self::Follow(_) => "sh.tangled.graph.follow", Self::Vouch(_) => "sh.tangled.graph.vouch", @@ -184,6 +188,7 @@ impl Record { Self::FeedComment(r) => Some(&r.created_at), Self::Reaction(r) => Some(&r.created_at), Self::Star(r) => Some(&r.created_at), + Self::Subscription(r) => Some(&r.created_at), Self::RefUpdate(_) => None, Self::Follow(r) => Some(&r.created_at), Self::Vouch(r) => Some(&r.created_at), @@ -210,6 +215,7 @@ impl Record { fn primary_edges(&self, source: &AtUri) -> Result, ExtractError> { match self { Self::Star(r) => star_edges(source, r), + Self::Subscription(r) => subscription_edges(source, r), Self::FeedComment(r) => feed_comment_edges(source, r), Self::Reaction(r) => reaction_edges(source, r), Self::Follow(r) => follow_edges(source, r), @@ -357,6 +363,23 @@ fn star_edges( Ok(one_edge("sh.tangled.feed.star", subject, source)) } +fn subscription_edges( + source: &AtUri, + record: &Subscription, +) -> Result, ExtractError> { + use crate::sh_tangled::feed::subscription::SubscriptionSubject; + let subject = match &record.subject { + SubscriptionSubject::Uri(v) => { + let Some(subject) = uri_subject_for_record(&v.uri) else { + return Ok(Vec::new()); + }; + subject + } + SubscriptionSubject::Repo(v) => SubjectRef::Did(v.did.clone()), + }; + Ok(one_edge("sh.tangled.feed.subscription", subject, source)) +} + fn reaction_edges( source: &AtUri, record: &Reaction, diff --git a/bobbin/crates/types/src/search.rs b/bobbin/crates/types/src/search.rs index afa21245..f548f7e5 100644 --- a/bobbin/crates/types/src/search.rs +++ b/bobbin/crates/types/src/search.rs @@ -65,6 +65,7 @@ impl SearchableRecord { Record::LabelDefinition(r) => Some(Self::LabelDefinition(r)), Record::Reaction(_) | Record::Star(_) + | Record::Subscription(_) | Record::RefUpdate(_) | Record::Follow(_) | Record::Vouch(_) diff --git a/cmd/cborgen/cborgen.go b/cmd/cborgen/cborgen.go index 36d45f61..be77c40c 100644 --- a/cmd/cborgen/cborgen.go +++ b/cmd/cborgen/cborgen.go @@ -6,7 +6,6 @@ import ( ) func main() { - genCfg := cbg.Gen{ MaxStringLength: 1_000_000, } @@ -29,6 +28,9 @@ func main() { tangled.FeedStar{}, tangled.FeedStar_Repo{}, tangled.FeedStar_String{}, + tangled.FeedSubscription{}, + tangled.FeedSubscription_Uri{}, + tangled.FeedSubscription_Repo{}, tangled.GitRefUpdate{}, tangled.GitRefUpdate_CommitCountBreakdown{}, tangled.GitRefUpdate_IndividualEmailCommitCount{}, @@ -73,5 +75,4 @@ func main() { ); err != nil { panic(err) } - } diff --git a/lexicons/feed/subscription.json b/lexicons/feed/subscription.json new file mode 100644 index 00000000..473b2c2a --- /dev/null +++ b/lexicons/feed/subscription.json @@ -0,0 +1,47 @@ +{ + "lexicon": 1, + "id": "sh.tangled.feed.subscription", + "defs": { + "main": { + "type": "record", + "key": "tid", + "record": { + "type": "object", + "required": [ + "subject", + "createdAt" + ], + "properties": { + "subject": { + "type": "union", + "refs": ["#uri", "#repo"], + "closed": true + }, + "collections": { + "type": "array", + "items": { "type": "string" }, + "description": "Optional collection NSIDs to filter which notifications are sent. Empty or absent means all collections." + }, + "createdAt": { + "type": "string", + "format": "datetime" + } + } + } + }, + "uri": { + "type": "object", + "required": ["uri"], + "properties": { + "uri": { "type": "string", "format": "at-uri" } + } + }, + "repo": { + "type": "object", + "required": ["did"], + "properties": { + "did": { "type": "string", "format": "did" } + } + } + } +}