Added Activity/IntransitiveActivity/Question Json marshaling and tests
This commit is contained in:
parent
8af476849f
commit
4599863fae
5 changed files with 868 additions and 262 deletions
25
activity.go
25
activity.go
|
@ -1,6 +1,7 @@
|
|||
package activitypub
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
@ -793,3 +794,27 @@ func FlattenActivityProperties(act *Activity) *Activity {
|
|||
act.Instrument = Flatten(act.Instrument)
|
||||
return act
|
||||
}
|
||||
|
||||
// MarshalJSON
|
||||
func (a Activity) MarshalJSON() ([]byte, error) {
|
||||
b := bytes.Buffer{}
|
||||
b.Write([]byte{'{'})
|
||||
|
||||
if !writeActivity(&b, a) {
|
||||
return nil, nil
|
||||
}
|
||||
b.Write([]byte{'}'})
|
||||
return b.Bytes(), nil
|
||||
}
|
||||
|
||||
// MarshalJSON
|
||||
func (i IntransitiveActivity) MarshalJSON() ([]byte, error) {
|
||||
b := bytes.Buffer{}
|
||||
b.Write([]byte{'{'})
|
||||
|
||||
if !writeIntransitiveActivity(&b, i) {
|
||||
return nil, nil
|
||||
}
|
||||
b.Write([]byte{'}'})
|
||||
return b.Bytes(), nil
|
||||
}
|
||||
|
|
485
activity_test.go
485
activity_test.go
|
@ -2,7 +2,9 @@ package activitypub
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestActivityNew(t *testing.T) {
|
||||
|
@ -976,4 +978,487 @@ func TestActivity_GetLink(t *testing.T) {
|
|||
|
||||
func TestActivity_GetType(t *testing.T) {
|
||||
t.Skipf("TODO")
|
||||
|
||||
func TestActivity_MarshalJSON(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 LinkOrIRI
|
||||
To ItemCollection
|
||||
Bto ItemCollection
|
||||
CC ItemCollection
|
||||
BCC ItemCollection
|
||||
Duration time.Duration
|
||||
Likes Item
|
||||
Shares Item
|
||||
Source Source
|
||||
Actor Item
|
||||
Target Item
|
||||
Result Item
|
||||
Origin Item
|
||||
Instrument Item
|
||||
Object Item
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
want []byte
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Empty",
|
||||
fields: fields{},
|
||||
want: nil,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "JustID",
|
||||
fields: fields{
|
||||
ID: ID("example.com"),
|
||||
},
|
||||
want: []byte(`{"id":"example.com"}`),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "JustType",
|
||||
fields: fields{
|
||||
Type: ActivityVocabularyType("myType"),
|
||||
},
|
||||
want: []byte(`{"type":"myType"}`),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "JustOneName",
|
||||
fields: fields{
|
||||
Name: NaturalLanguageValues{
|
||||
{Ref: NilLangRef, Value: "ana"},
|
||||
},
|
||||
},
|
||||
want: []byte(`{"name":"ana"}`),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "MoreNames",
|
||||
fields: fields{
|
||||
Name: NaturalLanguageValues{
|
||||
{Ref: "en", Value: "anna"},
|
||||
{Ref: "fr", Value: "anne"},
|
||||
},
|
||||
},
|
||||
want: []byte(`{"nameMap":{"en":"anna","fr":"anne"}}`),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "JustOneSummary",
|
||||
fields: fields{
|
||||
Summary: NaturalLanguageValues{
|
||||
{Ref: NilLangRef, Value: "test summary"},
|
||||
},
|
||||
},
|
||||
want: []byte(`{"summary":"test summary"}`),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "MoreSummaryEntries",
|
||||
fields: fields{
|
||||
Summary: NaturalLanguageValues{
|
||||
{Ref: "en", Value: "test summary"},
|
||||
{Ref: "fr", Value: "teste summary"},
|
||||
},
|
||||
},
|
||||
want: []byte(`{"summaryMap":{"en":"test summary","fr":"teste summary"}}`),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "JustOneContent",
|
||||
fields: fields{
|
||||
Content: NaturalLanguageValues{
|
||||
{Ref: NilLangRef, Value: "test content"},
|
||||
},
|
||||
},
|
||||
want: []byte(`{"content":"test content"}`),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "MoreContentEntries",
|
||||
fields: fields{
|
||||
Content: NaturalLanguageValues{
|
||||
{Ref: "en", Value: "test content"},
|
||||
{Ref: "fr", Value: "teste content"},
|
||||
},
|
||||
},
|
||||
want: []byte(`{"contentMap":{"en":"test content","fr":"teste content"}}`),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "MediaType",
|
||||
fields: fields{
|
||||
MediaType: MimeType("text/stupid"),
|
||||
},
|
||||
want: []byte(`{"mediaType":"text/stupid"}`),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Attachment",
|
||||
fields: fields{
|
||||
Attachment: &Object{
|
||||
ID: "some example",
|
||||
Type: VideoType,
|
||||
},
|
||||
},
|
||||
want: []byte(`{"attachment":{"id":"some example","type":"Video"}}`),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "AttributedTo",
|
||||
fields: fields{
|
||||
AttributedTo: &Actor{
|
||||
ID: "http://example.com/ana",
|
||||
Type: PersonType,
|
||||
},
|
||||
},
|
||||
want: []byte(`{"attributedTo":{"id":"http://example.com/ana","type":"Person"}}`),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "AttributedToDouble",
|
||||
fields: fields{
|
||||
AttributedTo: ItemCollection{
|
||||
&Actor{
|
||||
ID: "http://example.com/ana",
|
||||
Type: PersonType,
|
||||
},
|
||||
&Actor{
|
||||
ID: "http://example.com/GGG",
|
||||
Type: GroupType,
|
||||
},
|
||||
},
|
||||
},
|
||||
want: []byte(`{"attributedTo":[{"id":"http://example.com/ana","type":"Person"},{"id":"http://example.com/GGG","type":"Group"}]}`),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Source",
|
||||
fields: fields{
|
||||
Source: Source{
|
||||
MediaType: MimeType("text/plain"),
|
||||
Content: NaturalLanguageValues{},
|
||||
},
|
||||
},
|
||||
want: []byte(`{"source":{"mediaType":"text/plain"}}`),
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
a := Activity{
|
||||
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,
|
||||
Actor: tt.fields.Actor,
|
||||
Target: tt.fields.Target,
|
||||
Result: tt.fields.Result,
|
||||
Origin: tt.fields.Origin,
|
||||
Instrument: tt.fields.Instrument,
|
||||
Object: tt.fields.Object,
|
||||
}
|
||||
got, err := a.MarshalJSON()
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("MarshalJSON() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("MarshalJSON() got = %s, want %s", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIntransitiveActivity_MarshalJSON(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 LinkOrIRI
|
||||
To ItemCollection
|
||||
Bto ItemCollection
|
||||
CC ItemCollection
|
||||
BCC ItemCollection
|
||||
Duration time.Duration
|
||||
Likes Item
|
||||
Shares Item
|
||||
Source Source
|
||||
Actor CanReceiveActivities
|
||||
Target Item
|
||||
Result Item
|
||||
Origin Item
|
||||
Instrument Item
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
want []byte
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Empty",
|
||||
fields: fields{},
|
||||
want: nil,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "JustID",
|
||||
fields: fields{
|
||||
ID: ID("example.com"),
|
||||
},
|
||||
want: []byte(`{"id":"example.com"}`),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "JustType",
|
||||
fields: fields{
|
||||
Type: ActivityVocabularyType("myType"),
|
||||
},
|
||||
want: []byte(`{"type":"myType"}`),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "JustOneName",
|
||||
fields: fields{
|
||||
Name: NaturalLanguageValues{
|
||||
{Ref: NilLangRef, Value: "ana"},
|
||||
},
|
||||
},
|
||||
want: []byte(`{"name":"ana"}`),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "MoreNames",
|
||||
fields: fields{
|
||||
Name: NaturalLanguageValues{
|
||||
{Ref: "en", Value: "anna"},
|
||||
{Ref: "fr", Value: "anne"},
|
||||
},
|
||||
},
|
||||
want: []byte(`{"nameMap":{"en":"anna","fr":"anne"}}`),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "JustOneSummary",
|
||||
fields: fields{
|
||||
Summary: NaturalLanguageValues{
|
||||
{Ref: NilLangRef, Value: "test summary"},
|
||||
},
|
||||
},
|
||||
want: []byte(`{"summary":"test summary"}`),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "MoreSummaryEntries",
|
||||
fields: fields{
|
||||
Summary: NaturalLanguageValues{
|
||||
{Ref: "en", Value: "test summary"},
|
||||
{Ref: "fr", Value: "teste summary"},
|
||||
},
|
||||
},
|
||||
want: []byte(`{"summaryMap":{"en":"test summary","fr":"teste summary"}}`),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "JustOneContent",
|
||||
fields: fields{
|
||||
Content: NaturalLanguageValues{
|
||||
{Ref: NilLangRef, Value: "test content"},
|
||||
},
|
||||
},
|
||||
want: []byte(`{"content":"test content"}`),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "MoreContentEntries",
|
||||
fields: fields{
|
||||
Content: NaturalLanguageValues{
|
||||
{Ref: "en", Value: "test content"},
|
||||
{Ref: "fr", Value: "teste content"},
|
||||
},
|
||||
},
|
||||
want: []byte(`{"contentMap":{"en":"test content","fr":"teste content"}}`),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "MediaType",
|
||||
fields: fields{
|
||||
MediaType: MimeType("text/stupid"),
|
||||
},
|
||||
want: []byte(`{"mediaType":"text/stupid"}`),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Attachment",
|
||||
fields: fields{
|
||||
Attachment: &Object{
|
||||
ID: "some example",
|
||||
Type: VideoType,
|
||||
},
|
||||
},
|
||||
want: []byte(`{"attachment":{"id":"some example","type":"Video"}}`),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "AttributedTo",
|
||||
fields: fields{
|
||||
AttributedTo: &Actor{
|
||||
ID: "http://example.com/ana",
|
||||
Type: PersonType,
|
||||
},
|
||||
},
|
||||
want: []byte(`{"attributedTo":{"id":"http://example.com/ana","type":"Person"}}`),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "AttributedToDouble",
|
||||
fields: fields{
|
||||
AttributedTo: ItemCollection{
|
||||
&Actor{
|
||||
ID: "http://example.com/ana",
|
||||
Type: PersonType,
|
||||
},
|
||||
&Actor{
|
||||
ID: "http://example.com/GGG",
|
||||
Type: GroupType,
|
||||
},
|
||||
},
|
||||
},
|
||||
want: []byte(`{"attributedTo":[{"id":"http://example.com/ana","type":"Person"},{"id":"http://example.com/GGG","type":"Group"}]}`),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Source",
|
||||
fields: fields{
|
||||
Source: Source{
|
||||
MediaType: MimeType("text/plain"),
|
||||
Content: NaturalLanguageValues{},
|
||||
},
|
||||
},
|
||||
want: []byte(`{"source":{"mediaType":"text/plain"}}`),
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
i := IntransitiveActivity{
|
||||
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,
|
||||
Actor: tt.fields.Actor,
|
||||
Target: tt.fields.Target,
|
||||
Result: tt.fields.Result,
|
||||
Origin: tt.fields.Origin,
|
||||
Instrument: tt.fields.Instrument,
|
||||
}
|
||||
got, err := i.MarshalJSON()
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("MarshalJSON() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("MarshalJSON() got = %s, want %s", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
348
encoding.go
Normal file
348
encoding.go
Normal file
|
@ -0,0 +1,348 @@
|
|||
package activitypub
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
func writeProp(b *bytes.Buffer, name string, val []byte) (notEmpty bool) {
|
||||
if len(val) == 0 {
|
||||
return false
|
||||
}
|
||||
writePropName(b, name)
|
||||
return writeValue(b, val)
|
||||
}
|
||||
|
||||
func writePropName(b *bytes.Buffer, s string) (notEmpty bool) {
|
||||
if len(s) == 0 {
|
||||
return false
|
||||
}
|
||||
b.Write([]byte{'"'})
|
||||
b.WriteString(s)
|
||||
b.Write([]byte{'"', ':'})
|
||||
return true
|
||||
}
|
||||
|
||||
func writeValue(b *bytes.Buffer, s []byte) (notEmpty bool) {
|
||||
if len(s) == 0 {
|
||||
return false
|
||||
}
|
||||
b.Write(s)
|
||||
return true
|
||||
}
|
||||
|
||||
func writeNaturalLanguageProp(b *bytes.Buffer, n string, nl NaturalLanguageValues) (notEmpty bool) {
|
||||
l := nl.Count()
|
||||
if l > 1 {
|
||||
n += "Map"
|
||||
}
|
||||
if v, err := nl.MarshalJSON(); err == nil && len(v) > 0 {
|
||||
return writeProp(b, n, v)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func writeBoolProp(b *bytes.Buffer, n string, t bool) (notEmpty bool) {
|
||||
return writeProp(b, n, []byte(fmt.Sprintf("%t", t)))
|
||||
}
|
||||
func writeTimeProp(b *bytes.Buffer, n string, t time.Time) (notEmpty bool) {
|
||||
if v, err := t.MarshalJSON(); err == nil {
|
||||
return writeProp(b, n, v)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func writeDurationProp(b *bytes.Buffer, n string, d time.Duration) (notEmpty bool) {
|
||||
if v, err := marshalXSD(d); err == nil {
|
||||
return writeProp(b, n, v)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func writeIRIProp(b *bytes.Buffer, n string, i LinkOrIRI) (notEmpty bool) {
|
||||
url := i.GetLink()
|
||||
if len(url) == 0 {
|
||||
return false
|
||||
}
|
||||
writePropName(b, n)
|
||||
b.Write([]byte{'"'})
|
||||
b.Write([]byte(url))
|
||||
b.Write([]byte{'"'})
|
||||
return true
|
||||
}
|
||||
|
||||
func writeItemProp(b *bytes.Buffer, n string, i Item) (notEmpty bool) {
|
||||
if i == nil {
|
||||
return notEmpty
|
||||
}
|
||||
if i.IsObject() {
|
||||
OnObject(i, func(o *Object) error {
|
||||
v, err := o.MarshalJSON()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
notEmpty = writeProp(b, n, v)
|
||||
return nil
|
||||
})
|
||||
} else if i.IsCollection() {
|
||||
OnCollection(i, func(c CollectionInterface) error {
|
||||
notEmpty = writeItemCollectionProp(b, n, c.Collection()) || notEmpty
|
||||
return nil
|
||||
})
|
||||
}
|
||||
return notEmpty
|
||||
}
|
||||
|
||||
func writeItemCollectionProp(b *bytes.Buffer, n string, col ItemCollection) (notEmpty bool) {
|
||||
if len(col) == 0 {
|
||||
return notEmpty
|
||||
}
|
||||
writePropName(b, n)
|
||||
writeComma := func() { b.WriteString(",") }
|
||||
writeCommaIfNotEmpty := func(notEmpty bool) {
|
||||
if notEmpty {
|
||||
writeComma()
|
||||
}
|
||||
}
|
||||
b.Write([]byte{'['})
|
||||
for _, i := range col {
|
||||
if i.IsObject() {
|
||||
OnObject(i, func(o *Object) error {
|
||||
v, err := o.MarshalJSON()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeValue(b, v) || notEmpty
|
||||
return nil
|
||||
})
|
||||
} else if i.IsLink() {
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeValue(b, []byte(i.GetLink())) || notEmpty
|
||||
}
|
||||
}
|
||||
b.Write([]byte{']'})
|
||||
return notEmpty
|
||||
}
|
||||
|
||||
func writeObject(b *bytes.Buffer, o Object) (notEmpty bool) {
|
||||
writeComma := func() { b.WriteString(",") }
|
||||
writeCommaIfNotEmpty := func(notEmpty bool) {
|
||||
if notEmpty {
|
||||
writeComma()
|
||||
}
|
||||
}
|
||||
if v, err := o.ID.MarshalJSON(); err == nil && len(v) > 0 {
|
||||
notEmpty = writeProp(b, "id", v) || notEmpty
|
||||
}
|
||||
if v, err := o.Type.MarshalJSON(); err == nil && len(v) > 0 {
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeProp(b, "type", v) || notEmpty
|
||||
}
|
||||
if v, err := o.MediaType.MarshalJSON(); err == nil && len(v) > 0 {
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeProp(b, "mediaType", v) || notEmpty
|
||||
}
|
||||
if len(o.Name) > 0 {
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeNaturalLanguageProp(b, "name", o.Name) || notEmpty
|
||||
}
|
||||
if len(o.Summary) > 0 {
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeNaturalLanguageProp(b, "summary", o.Summary) || notEmpty
|
||||
}
|
||||
if len(o.Content) > 0 {
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeNaturalLanguageProp(b, "content", o.Content) || notEmpty
|
||||
}
|
||||
if o.Attachment != nil {
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeItemProp(b, "attachment", o.Attachment) || notEmpty
|
||||
}
|
||||
if o.AttributedTo != nil {
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeItemProp(b, "attributedTo", o.AttributedTo) || notEmpty
|
||||
}
|
||||
if o.Audience != nil {
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeItemProp(b, "audience", o.Audience) || notEmpty
|
||||
}
|
||||
if o.Context != nil {
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeItemProp(b, "context", o.Context) || notEmpty
|
||||
}
|
||||
if o.Generator != nil {
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeItemProp(b, "generator", o.Generator) || notEmpty
|
||||
}
|
||||
if o.Icon != nil {
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeItemProp(b, "icon", o.Icon) || notEmpty
|
||||
}
|
||||
if o.Image != nil {
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeItemProp(b, "image", o.Image) || notEmpty
|
||||
}
|
||||
if o.InReplyTo != nil {
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeItemProp(b, "inReplyTo", o.InReplyTo) || notEmpty
|
||||
}
|
||||
if o.Location != nil {
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeItemProp(b, "location", o.Location) || notEmpty
|
||||
}
|
||||
if o.Preview != nil {
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeItemProp(b, "preview", o.Preview) || notEmpty
|
||||
}
|
||||
if o.Replies != nil {
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeItemProp(b, "replies", o.Replies) || notEmpty
|
||||
}
|
||||
if o.Tag != nil {
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeItemProp(b, "tag", o.Tag) || notEmpty
|
||||
}
|
||||
if o.URL != nil {
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeIRIProp(b, "url", o.URL) || notEmpty
|
||||
}
|
||||
if o.To != nil {
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeItemProp(b, "to", o.To) || notEmpty
|
||||
}
|
||||
if o.Bto != nil {
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeItemProp(b, "bto", o.Bto) || notEmpty
|
||||
}
|
||||
if o.CC != nil {
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeItemProp(b, "cc", o.CC) || notEmpty
|
||||
}
|
||||
if o.BCC != nil {
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeItemProp(b, "bcc", o.BCC) || notEmpty
|
||||
}
|
||||
if !o.Published.IsZero() {
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeTimeProp(b, "published", o.Published) || notEmpty
|
||||
}
|
||||
if !o.Updated.IsZero() {
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeTimeProp(b, "updated", o.Updated) || notEmpty
|
||||
}
|
||||
if !o.StartTime.IsZero() {
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeTimeProp(b, "startTime", o.StartTime) || notEmpty
|
||||
}
|
||||
if !o.EndTime.IsZero() {
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeTimeProp(b, "endTime", o.EndTime) || notEmpty
|
||||
}
|
||||
if o.Duration != 0 {
|
||||
// TODO(marius): maybe don't use 0 as a nil value for Object types
|
||||
// which can have a valid duration of 0 - (Video, Audio, etc)
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeDurationProp(b, "duration", o.Duration) || notEmpty
|
||||
}
|
||||
if o.Likes != nil {
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeItemProp(b, "likes", o.Likes) || notEmpty
|
||||
}
|
||||
if o.Shares != nil {
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeItemProp(b, "shares", o.Shares) || notEmpty
|
||||
}
|
||||
if v, err := o.Source.MarshalJSON(); err == nil && len(v) > 0 {
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeProp(b, "source", v) || notEmpty
|
||||
}
|
||||
return notEmpty
|
||||
}
|
||||
|
||||
func writeActivity(b *bytes.Buffer, a Activity) (notEmpty bool) {
|
||||
writeComma := func() { b.WriteString(",") }
|
||||
writeCommaIfNotEmpty := func(notEmpty bool) {
|
||||
if notEmpty {
|
||||
writeComma()
|
||||
}
|
||||
}
|
||||
|
||||
OnIntransitiveActivity(a, func(i *IntransitiveActivity) error {
|
||||
if i == nil {
|
||||
return nil
|
||||
}
|
||||
notEmpty = writeIntransitiveActivity(b, *i) || notEmpty
|
||||
return nil
|
||||
})
|
||||
if a.Object != nil {
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeItemProp(b, "object", a.Object) || notEmpty
|
||||
}
|
||||
return notEmpty
|
||||
}
|
||||
|
||||
func writeIntransitiveActivity(b *bytes.Buffer, i IntransitiveActivity) (notEmpty bool) {
|
||||
writeComma := func() { b.WriteString(",") }
|
||||
writeCommaIfNotEmpty := func(notEmpty bool) {
|
||||
if notEmpty {
|
||||
writeComma()
|
||||
}
|
||||
}
|
||||
OnObject(i, func(o *Object) error {
|
||||
if o == nil {
|
||||
return nil
|
||||
}
|
||||
notEmpty = writeObject(b, *o) || notEmpty
|
||||
return nil
|
||||
})
|
||||
if i.Actor != nil {
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeItemProp(b, "actor", i.Actor) || notEmpty
|
||||
}
|
||||
if i.Target != nil {
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeItemProp(b, "target", i.Target) || notEmpty
|
||||
}
|
||||
if i.Result != nil {
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeItemProp(b, "result", i.Result) || notEmpty
|
||||
}
|
||||
if i.Origin != nil {
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeItemProp(b, "origin", i.Origin) || notEmpty
|
||||
}
|
||||
if i.Instrument != nil {
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeItemProp(b, "instrument", i.Instrument) || notEmpty
|
||||
}
|
||||
return notEmpty
|
||||
}
|
||||
|
||||
func writeQuestion(b *bytes.Buffer, q Question) (notEmpty bool) {
|
||||
writeComma := func() { b.WriteString(",") }
|
||||
writeCommaIfNotEmpty := func(notEmpty bool) {
|
||||
if notEmpty {
|
||||
writeComma()
|
||||
}
|
||||
}
|
||||
|
||||
OnIntransitiveActivity(q, func(i *IntransitiveActivity) error {
|
||||
if i == nil {
|
||||
return nil
|
||||
}
|
||||
notEmpty = writeIntransitiveActivity(b, *i) || notEmpty
|
||||
return nil
|
||||
})
|
||||
if q.OneOf != nil {
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeItemProp(b, "oneOf", q.OneOf) || notEmpty
|
||||
} else if q.AnyOf != nil {
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeItemProp(b, "oneOf", q.OneOf) || notEmpty
|
||||
}
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeBoolProp(b, "closed", q.Closed) || notEmpty
|
||||
return notEmpty
|
||||
}
|
|
@ -3,6 +3,7 @@ package activitypub
|
|||
import (
|
||||
"errors"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// IntransitiveActivity Instances of IntransitiveActivity are a subtype of Activity representing intransitive actions.
|
||||
|
@ -256,6 +257,14 @@ func IntransitiveActivityNew(id ID, typ ActivityVocabularyType) *IntransitiveAct
|
|||
// ToIntransitiveActivity
|
||||
func ToIntransitiveActivity(it Item) (*IntransitiveActivity, error) {
|
||||
switch i := it.(type) {
|
||||
case *Activity:
|
||||
return (*IntransitiveActivity)(unsafe.Pointer(i)), nil
|
||||
case Activity:
|
||||
return (*IntransitiveActivity)(unsafe.Pointer(&i)), nil
|
||||
case *Question:
|
||||
return (*IntransitiveActivity)(unsafe.Pointer(i)), nil
|
||||
case Question:
|
||||
return (*IntransitiveActivity)(unsafe.Pointer(&i)), nil
|
||||
case *IntransitiveActivity:
|
||||
return i, nil
|
||||
case IntransitiveActivity:
|
||||
|
|
263
object.go
263
object.go
|
@ -320,275 +320,14 @@ func (o *Object) UnmarshalJSON(data []byte) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func writeProp(b *bytes.Buffer, name string, val []byte) (notEmpty bool) {
|
||||
notEmpty = false
|
||||
if len(val) == 0 {
|
||||
return notEmpty
|
||||
}
|
||||
writePropName(b, name)
|
||||
return writeValue(b, val)
|
||||
}
|
||||
|
||||
func writePropName(b *bytes.Buffer, s string) (notEmpty bool) {
|
||||
if len(s) == 0 {
|
||||
return false
|
||||
}
|
||||
b.Write([]byte{'"'})
|
||||
l, err := b.WriteString(s)
|
||||
b.Write([]byte{'"', ':'})
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if l <= 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func writeValue(b *bytes.Buffer, s []byte) (notEmpty bool) {
|
||||
l, err := b.Write(s)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if l <= 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func writeNaturalLanguageProp(b *bytes.Buffer, n string, nl NaturalLanguageValues) (notEmpty bool) {
|
||||
l := nl.Count()
|
||||
if l > 1 {
|
||||
n += "Map"
|
||||
}
|
||||
if v, err := nl.MarshalJSON(); err == nil && len(v) > 0 {
|
||||
notEmpty = writeProp(b, n, v)
|
||||
}
|
||||
return notEmpty
|
||||
}
|
||||
|
||||
func writeTimeProp(b *bytes.Buffer, n string, t time.Time) (notEmpty bool) {
|
||||
v, err := t.MarshalJSON()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return writeProp(b, n, v)
|
||||
}
|
||||
func writeDurationProp(b *bytes.Buffer, n string, d time.Duration) (notEmpty bool) {
|
||||
if v, err := marshalXSD(d); err == nil {
|
||||
return writeProp(b, n, v)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func writeIRIProp(b *bytes.Buffer, n string, i LinkOrIRI) (notEmpty bool) {
|
||||
url := i.GetLink()
|
||||
if len(url) > 0 {
|
||||
writePropName(b, n)
|
||||
b.Write([]byte{'"'})
|
||||
b.Write([]byte(url))
|
||||
b.Write([]byte{'"'})
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func writeItemProp(b *bytes.Buffer, n string, i Item) (notEmpty bool) {
|
||||
notEmpty = false
|
||||
if i == nil {
|
||||
return notEmpty
|
||||
}
|
||||
if i.IsObject() {
|
||||
OnObject(i, func(o *Object) error {
|
||||
v, err := o.MarshalJSON()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
notEmpty = writeProp(b, n, v)
|
||||
return nil
|
||||
})
|
||||
} else if i.IsCollection() {
|
||||
OnCollection(i, func(c CollectionInterface) error {
|
||||
notEmpty = writeItemCollectionProp(b, n, c.Collection())
|
||||
return nil
|
||||
})
|
||||
}
|
||||
return notEmpty
|
||||
}
|
||||
|
||||
func writeItemCollectionProp(b *bytes.Buffer, n string, col ItemCollection) (notEmpty bool) {
|
||||
notEmpty = false
|
||||
if len(col) == 0 {
|
||||
return notEmpty
|
||||
}
|
||||
writePropName(b, n)
|
||||
writeComma := func() { b.WriteString(",") }
|
||||
writeCommaIfNotEmpty := func(notEmpty bool) {
|
||||
if notEmpty {
|
||||
writeComma()
|
||||
}
|
||||
}
|
||||
b.Write([]byte{'['})
|
||||
for _, i := range col {
|
||||
if i.IsObject() {
|
||||
OnObject(i, func(o *Object) error {
|
||||
v, err := o.MarshalJSON()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeValue(b, v)
|
||||
return nil
|
||||
})
|
||||
} else if i.IsLink() {
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeValue(b, []byte(i.GetLink()))
|
||||
}
|
||||
}
|
||||
b.Write([]byte{']'})
|
||||
return notEmpty
|
||||
}
|
||||
|
||||
// MarshalJSON
|
||||
func (o Object) MarshalJSON() ([]byte, error) {
|
||||
b := bytes.Buffer{}
|
||||
notEmpty := false
|
||||
b.Write([]byte{'{'})
|
||||
|
||||
writeComma := func() { b.WriteString(",") }
|
||||
writeCommaIfNotEmpty := func(notEmpty bool) {
|
||||
if notEmpty {
|
||||
writeComma()
|
||||
}
|
||||
}
|
||||
if v, err := o.ID.MarshalJSON(); err == nil && len(v) > 0 {
|
||||
notEmpty = writeProp(&b, "id", v)
|
||||
}
|
||||
if v, err := o.Type.MarshalJSON(); err == nil && len(v) > 0 {
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeProp(&b, "type", v)
|
||||
}
|
||||
if v, err := o.MediaType.MarshalJSON(); err == nil && len(v) > 0 {
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeProp(&b, "mediaType", v)
|
||||
}
|
||||
if len(o.Name) > 0 {
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeNaturalLanguageProp(&b, "name", o.Name)
|
||||
}
|
||||
if len(o.Summary) > 0 {
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeNaturalLanguageProp(&b, "summary", o.Summary)
|
||||
}
|
||||
if len(o.Content) > 0 {
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeNaturalLanguageProp(&b, "content", o.Content)
|
||||
}
|
||||
if o.Attachment != nil {
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeItemProp(&b, "attachment", o.Attachment)
|
||||
}
|
||||
if o.AttributedTo != nil {
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeItemProp(&b, "attributedTo", o.AttributedTo)
|
||||
}
|
||||
if o.Audience != nil {
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeItemProp(&b, "audience", o.Audience)
|
||||
}
|
||||
if o.Context != nil {
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeItemProp(&b, "context", o.Context)
|
||||
}
|
||||
if o.Generator != nil {
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeItemProp(&b, "generator", o.Generator)
|
||||
}
|
||||
if o.Icon != nil {
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeItemProp(&b, "icon", o.Icon)
|
||||
}
|
||||
if o.Image != nil {
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeItemProp(&b, "image", o.Image)
|
||||
}
|
||||
if o.InReplyTo != nil {
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeItemProp(&b, "inReplyTo", o.InReplyTo)
|
||||
}
|
||||
if o.Location != nil {
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeItemProp(&b, "location", o.Location)
|
||||
}
|
||||
if o.Preview != nil {
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeItemProp(&b, "preview", o.Preview)
|
||||
}
|
||||
if o.Replies != nil {
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeItemProp(&b, "replies", o.Replies)
|
||||
}
|
||||
if o.Tag != nil {
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeItemProp(&b, "tag", o.Tag)
|
||||
}
|
||||
if o.URL != nil {
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeIRIProp(&b, "url", o.URL)
|
||||
}
|
||||
if o.To != nil {
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeItemProp(&b, "to", o.To)
|
||||
}
|
||||
if o.Bto != nil {
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeItemProp(&b, "bto", o.Bto)
|
||||
}
|
||||
if o.CC != nil {
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeItemProp(&b, "cc", o.CC)
|
||||
}
|
||||
if o.BCC != nil {
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeItemProp(&b, "bcc", o.BCC)
|
||||
}
|
||||
notEmpty = writeObject(&b, o)
|
||||
|
||||
if !o.Published.IsZero() {
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeTimeProp(&b, "published", o.Published)
|
||||
}
|
||||
if !o.Updated.IsZero() {
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeTimeProp(&b, "updated", o.Updated)
|
||||
}
|
||||
if !o.StartTime.IsZero() {
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeTimeProp(&b, "startTime", o.StartTime)
|
||||
}
|
||||
if !o.EndTime.IsZero() {
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeTimeProp(&b, "endTime", o.EndTime)
|
||||
}
|
||||
if o.Duration != 0 {
|
||||
// TODO(marius): maybe don't use 0 as a nil value for Object types
|
||||
// which can have a valid duration of 0 - (Video, Audio, etc)
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeDurationProp(&b, "duration", o.Duration)
|
||||
}
|
||||
|
||||
if o.Likes != nil {
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeItemProp(&b, "likes", o.Likes)
|
||||
}
|
||||
if o.Shares != nil {
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeItemProp(&b, "shares", o.Shares)
|
||||
}
|
||||
|
||||
if v, err := o.Source.MarshalJSON(); err == nil && len(v) > 0 {
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeProp(&b, "source", v)
|
||||
}
|
||||
if notEmpty {
|
||||
b.Write([]byte{'}'})
|
||||
return b.Bytes(), nil
|
||||
|
|
Reference in a new issue