Adding gob encoding for an object

This commit is contained in:
Marius Orcsik 2022-01-02 16:25:28 +01:00
parent 30317634f5
commit 98500a23a4
No known key found for this signature in database
GPG key ID: DBF5E47F5DBC4D21
2 changed files with 470 additions and 10 deletions

377
object.go
View file

@ -309,32 +309,389 @@ func (o Object) MarshalJSON() ([]byte, error) {
return nil, nil
}
/*
// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface.
func (o *Object) UnmarshalBinary(data []byte) error {
return errors.New(fmt.Sprintf("UnmarshalBinary is not implemented for %T", *o))
return o.GobDecode(data)
}
// MarshalBinary implements the encoding.BinaryMarshaler interface.
func (o Object) MarshalBinary() ([]byte, error) {
w := &bytes.Buffer{}
enc := gobEncoder{ w: w, enc: gob.NewEncoder(w) }
if _, err := enc.writeObjectGobValue(o); err != nil {
return nil, err
return o.GobEncode()
}
func gobDecodeItem(it Item, data []byte) error {
return nil
}
func gobEncodeItemCollection(g *gob.Encoder, col ItemCollection) error {
return g.Encode(col)
}
func gobEncodeItem(it Item) ([]byte, error) {
b := bytes.Buffer{}
var err error
if IsIRI(it) {
g := gob.NewEncoder(&b)
err = gobEncodeStringLikeType(g, []byte(it.GetLink()))
}
return w.Bytes(), nil
if IsItemCollection(it) {
g := gob.NewEncoder(&b)
err = OnItemCollection(it, func(col *ItemCollection) error {
return gobEncodeItemCollection(g, *col)
})
}
switch it.GetType() {
case "", ObjectType, ArticleType, AudioType, DocumentType, EventType, ImageType, NoteType, PageType, VideoType:
err = OnObject(it, func(ob *Object) error {
bytes, err := ob.GobEncode()
b.Write(bytes)
return err
})
case LinkType, MentionType:
err = OnLink(it, func(l *Link) error {
bytes, err := l.GobEncode()
b.Write(bytes)
return err
})
case ActivityType, AcceptType, AddType, AnnounceType, BlockType, CreateType, DeleteType, DislikeType,
FlagType, FollowType, IgnoreType, InviteType, JoinType, LeaveType, LikeType, ListenType, MoveType, OfferType,
RejectType, ReadType, RemoveType, TentativeRejectType, TentativeAcceptType, UndoType, UpdateType, ViewType:
err = OnActivity(it, func(act *Activity) error {
bytes, err := act.GobEncode()
b.Write(bytes)
return err
})
case IntransitiveActivityType, ArriveType, TravelType:
err = OnIntransitiveActivity(it, func(act *IntransitiveActivity) error {
bytes, err := act.GobEncode()
b.Write(bytes)
return err
})
case ActorType, ApplicationType, GroupType, OrganizationType, PersonType, ServiceType:
err = OnActor(it, func(a *Actor) error {
bytes, err := a.GobEncode()
b.Write(bytes)
return err
})
case CollectionType:
err = OnCollection(it, func(c *Collection) error {
return nil
})
case OrderedCollectionType:
err = OnOrderedCollection(it, func(c *OrderedCollection) error {
return nil
})
case CollectionPageType:
err = OnCollectionPage(it, func(p *CollectionPage) error {
return nil
})
case OrderedCollectionPageType:
err = OnOrderedCollectionPage(it, func(p *OrderedCollectionPage) error {
return nil
})
case PlaceType:
err = OnPlace(it, func(p *Place) error {
return nil
})
case ProfileType:
err = OnProfile(it, func(p *Profile) error {
return nil
})
case RelationshipType:
err = OnRelationship(it, func(r *Relationship) error {
return nil
})
case TombstoneType:
err = OnTombstone(it, func(t *Tombstone) error {
return nil
})
case QuestionType:
err = OnQuestion(it, func(q *Question) error {
return nil
})
}
return b.Bytes(), err
}
func mapObjectProperties(mm map[string][]byte, o *Object) (hasData bool, err error) {
if len(o.ID) > 0 {
if mm["id"], err = o.ID.GobEncode(); err != nil {
return hasData, err
}
hasData = true
}
if len(o.Type) > 0 {
if mm["type"], err = o.Type.GobEncode(); err != nil {
return hasData, err
}
hasData = true
}
if len(o.MediaType) > 0 {
if mm["mediaType"], err = o.MediaType.GobEncode(); err != nil {
return hasData, err
}
hasData = true
}
if len(o.Name) > 0 {
if mm["name"], err = o.Name.GobEncode(); err != nil {
return hasData, err
}
hasData = true
}
if o.Attachment != nil {
if mm["attachment"], err = gobEncodeItem(o.Attachment); err != nil {
return hasData, err
}
hasData = true
}
if o.AttributedTo != nil {
if mm["attributedTo"], err = gobEncodeItem(o.AttributedTo); err != nil {
return hasData, err
}
hasData = true
}
if o.Audience != nil {
if mm["audience"], err = gobEncodeItem(o.Audience); err != nil {
return hasData, err
}
hasData = true
}
if o.Content != nil {
if mm["content"], err = o.Content.GobEncode(); err != nil {
return hasData, err
}
hasData = true
}
if o.Context != nil {
if mm["context"], err = gobEncodeItem(o.Context); err != nil {
return hasData, err
}
hasData = true
}
if len(o.MediaType) > 0 {
if mm["mediaType"], err = o.MediaType.GobEncode(); err != nil {
return hasData, err
}
hasData = true
}
if !o.EndTime.IsZero() {
if mm["endTime"], err = o.EndTime.GobEncode(); err != nil {
return hasData, err
}
hasData = true
}
if o.Generator != nil {
if mm["generator"], err = gobEncodeItem(o.Generator); err != nil {
return hasData, err
}
hasData = true
}
if o.Icon != nil {
if mm["icon"], err = gobEncodeItem(o.Icon); err != nil {
return hasData, err
}
hasData = true
}
if o.Image != nil {
if mm["image"], err = gobEncodeItem(o.Image); err != nil {
return hasData, err
}
hasData = true
}
if o.InReplyTo != nil {
if mm["inReplyTo"], err = gobEncodeItem(o.InReplyTo); err != nil {
return hasData, err
}
hasData = true
}
if o.Location != nil {
if mm["location"], err = gobEncodeItem(o.Location); err != nil {
return hasData, err
}
hasData = true
}
if o.Preview != nil {
if mm["preview"], err = gobEncodeItem(o.Preview); err != nil {
return hasData, err
}
hasData = true
}
if !o.Published.IsZero() {
if mm["published"], err = o.Published.GobEncode(); err != nil {
return hasData, err
}
hasData = true
}
if o.Replies != nil {
if mm["replies"], err = gobEncodeItem(o.Replies); err != nil {
return hasData, err
}
hasData = true
}
if !o.StartTime.IsZero() {
if mm["startTime"], err = o.StartTime.GobEncode(); err != nil {
return hasData, err
}
hasData = true
}
if len(o.Summary) > 0 {
if mm["summary"], err = o.Summary.GobEncode(); err != nil {
return hasData, err
}
hasData = true
}
if o.Tag != nil {
if mm["tag"], err = gobEncodeItem(o.Tag); err != nil {
return hasData, err
}
hasData = true
}
if !o.Updated.IsZero() {
if mm["updated"], err = o.Updated.GobEncode(); err != nil {
return hasData, err
}
hasData = true
}
if o.Tag != nil {
if mm["tag"], err = gobEncodeItem(o.Tag); err != nil {
return hasData, err
}
hasData = true
}
if !o.Updated.IsZero() {
if mm["updated"], err = o.Updated.GobEncode(); err != nil {
return hasData, err
}
hasData = true
}
if o.URL != nil {
if mm["url"], err = gobEncodeItem(o.URL); err != nil {
return hasData, err
}
hasData = true
}
if o.To != nil {
if mm["to"], err = gobEncodeItem(o.To); err != nil {
return hasData, err
}
hasData = true
}
if o.Bto != nil {
if mm["bto"], err = gobEncodeItem(o.Bto); err != nil {
return hasData, err
}
hasData = true
}
if o.CC != nil {
if mm["cc"], err = gobEncodeItem(o.CC); err != nil {
return hasData, err
}
hasData = true
}
if o.BCC != nil {
if mm["bcc"], err = gobEncodeItem(o.BCC); err != nil {
return hasData, err
}
hasData = true
}
if o.Duration > 0 {
if mm["duration"], err = gobEncodeInt64(int64(o.Duration)); err != nil {
return hasData, err
}
hasData = true
}
if o.Likes != nil {
if mm["likes"], err = gobEncodeItem(o.Likes); err != nil {
return hasData, err
}
hasData = true
}
if o.Shares != nil {
if mm["shares"], err = gobEncodeItem(o.Shares); err != nil {
return hasData, err
}
hasData = true
}
if o.Shares != nil {
if mm["shares"], err = gobEncodeItem(o.Shares); err != nil {
return hasData, err
}
hasData = true
}
if len(o.Source.MediaType)+len(o.Source.Content) > 0 {
if mm["source"], err = o.Source.GobEncode(); err != nil {
return hasData, err
}
hasData = true
}
return hasData, nil
}
// GobEncode
func (o Object) GobEncode() ([]byte, error) {
return o.MarshalBinary()
mm := make(map[string][]byte)
hasData, err := mapObjectProperties(mm, &o)
if err != nil {
return nil, err
}
if !hasData {
return []byte{}, nil
}
bb := bytes.Buffer{}
g := gob.NewEncoder(&bb)
if err := g.Encode(mm); err != nil {
return nil, err
}
return bb.Bytes(), nil
}
func unmapObjectProperties(mm map[string][]byte, o *Object) error {
if raw, ok := mm["id"]; ok {
if err := o.ID.GobDecode(raw); err != nil {
return err
}
}
if raw, ok := mm["type"]; ok {
if err := o.Type.GobDecode(raw); err != nil {
return err
}
}
if raw, ok := mm["mediaType"]; ok {
if err := o.MediaType.GobDecode(raw); err != nil {
return err
}
}
if raw, ok := mm["name"]; ok {
if err := o.Name.GobDecode(raw); err != nil {
return err
}
}
if raw, ok := mm["attachment"]; ok {
if err := gobDecodeItem(o.Attachment, raw); err != nil {
return err
}
}
if raw, ok := mm["source"]; ok {
if err := o.Source.GobDecode(raw); err != nil {
return err
}
}
return nil
}
// GobDecode
func (o *Object) GobDecode(data []byte) error {
return o.UnmarshalBinary(data)
if len(data) == 0 {
return nil
}
mm := make(map[string][]byte)
g := gob.NewDecoder(bytes.NewReader(data))
if err := g.Decode(&mm); err != nil {
return err
}
return unmapObjectProperties(mm, o)
}
*/
// Recipients performs recipient de-duplication on the Object's To, Bto, CC and BCC properties
func (o *Object) Recipients() ItemCollection {

View file

@ -1,6 +1,7 @@
package activitypub
import (
"bytes"
"reflect"
"testing"
"time"
@ -976,3 +977,105 @@ func TestObject_Equals(t *testing.T) {
})
}
}
func TestObject_GobEncode(t *testing.T) {
type fields struct {
ID ID
Type ActivityVocabularyType
Name NaturalLanguageValues
Attachment Item
AttributedTo Item
Audience ItemCollection
Content NaturalLanguageValues
Context Item
MediaType MimeType
EndTime time.Time
Generator Item
Icon Item
Image Item
InReplyTo Item
Location Item
Preview Item
Published time.Time
Replies Item
StartTime time.Time
Summary NaturalLanguageValues
Tag ItemCollection
Updated time.Time
URL Item
To ItemCollection
Bto ItemCollection
CC ItemCollection
BCC ItemCollection
Duration time.Duration
Likes Item
Shares Item
Source Source
}
tests := []struct {
name string
fields fields
want []byte
wantErr bool
}{
{
name: "empty",
fields: fields{},
want: []byte{},
wantErr: false,
},
// NOTE(marius): this doesn't really work because the types are not consistent between gobValue
// and our used encoding method
//{
// name: "with ID",
// fields: fields{ID: ID("https://example.com")},
// want: gobValue(Object{ID: "https://example.com"}),
// wantErr: false,
//},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
o := Object{
ID: tt.fields.ID,
Type: tt.fields.Type,
Name: tt.fields.Name,
Attachment: tt.fields.Attachment,
AttributedTo: tt.fields.AttributedTo,
Audience: tt.fields.Audience,
Content: tt.fields.Content,
Context: tt.fields.Context,
MediaType: tt.fields.MediaType,
EndTime: tt.fields.EndTime,
Generator: tt.fields.Generator,
Icon: tt.fields.Icon,
Image: tt.fields.Image,
InReplyTo: tt.fields.InReplyTo,
Location: tt.fields.Location,
Preview: tt.fields.Preview,
Published: tt.fields.Published,
Replies: tt.fields.Replies,
StartTime: tt.fields.StartTime,
Summary: tt.fields.Summary,
Tag: tt.fields.Tag,
Updated: tt.fields.Updated,
URL: tt.fields.URL,
To: tt.fields.To,
Bto: tt.fields.Bto,
CC: tt.fields.CC,
BCC: tt.fields.BCC,
Duration: tt.fields.Duration,
Likes: tt.fields.Likes,
Shares: tt.fields.Shares,
Source: tt.fields.Source,
}
got, err := o.GobEncode()
if (err != nil) != tt.wantErr {
t.Errorf("GobEncode() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !bytes.Equal(got, tt.want) {
t.Errorf("GobEncode() got/want =\n%v\n%v\n", got, tt.want)
}
})
}
}