Improvements to Object, NaturalLanguage and Source Json marshaling
Improvements to tests
This commit is contained in:
parent
88cce1d2f9
commit
8420879a85
4 changed files with 407 additions and 88 deletions
|
@ -4,7 +4,6 @@ import (
|
|||
"bytes"
|
||||
"fmt"
|
||||
"github.com/buger/jsonparser"
|
||||
json "github.com/go-ap/jsonld"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
@ -73,21 +72,47 @@ func (n *NaturalLanguageValues) Set(ref LangRef, v string) error {
|
|||
|
||||
// MarshalJSON serializes the NaturalLanguageValues into JSON
|
||||
func (n NaturalLanguageValues) MarshalJSON() ([]byte, error) {
|
||||
if len(n) == 0 {
|
||||
return json.Marshal(nil)
|
||||
}
|
||||
if len(n) == 1 {
|
||||
v := n[0]
|
||||
if v.Ref == NilLangRef {
|
||||
return v.MarshalJSON()
|
||||
}
|
||||
}
|
||||
mm := make(map[LangRef]string)
|
||||
for _, val := range n {
|
||||
mm[val.Ref] = val.Value
|
||||
l := len(n)
|
||||
if l == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return json.Marshal(mm)
|
||||
b := bytes.Buffer{}
|
||||
if l == 1 {
|
||||
v := n[0]
|
||||
if len(v.Value) > 0 {
|
||||
v.Value = string(unescape([]byte(v.Value)))
|
||||
ll, err := b.WriteString(strconv.Quote(v.Value))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ll == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
return b.Bytes(), nil
|
||||
}
|
||||
}
|
||||
b.Write([]byte{'{'})
|
||||
empty := true
|
||||
for _, val := range n {
|
||||
if len(val.Ref) == 0 || len(val.Value) == 0 {
|
||||
continue
|
||||
}
|
||||
if !empty {
|
||||
b.Write([]byte{','})
|
||||
}
|
||||
if v, err := val.MarshalJSON(); err == nil && len(v) > 0 {
|
||||
l, err := b.Write(v)
|
||||
if err == nil && l > 0 {
|
||||
empty = false
|
||||
}
|
||||
}
|
||||
}
|
||||
b.Write([]byte{'}'})
|
||||
if !empty {
|
||||
return b.Bytes(), nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// First returns the first element in the array
|
||||
|
@ -165,7 +190,7 @@ func (l *LangRefValue) UnmarshalText(data []byte) error {
|
|||
// MarshalJSON serializes the LangRefValue into JSON
|
||||
func (l LangRefValue) MarshalJSON() ([]byte, error) {
|
||||
buf := bytes.Buffer{}
|
||||
if l.Ref != NilLangRef {
|
||||
if l.Ref != NilLangRef && len(l.Ref) > 0{
|
||||
if l.Value == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ func TestNaturalLanguageValue_MarshalJSON(t *testing.T) {
|
|||
if err1 != nil {
|
||||
t.Errorf("Error: '%s'", err1)
|
||||
}
|
||||
txt := `{"en":"the test"}`
|
||||
txt := `"the test"`
|
||||
if txt != string(out1) {
|
||||
t.Errorf("Different marshal result '%s', instead of '%s'", out1, txt)
|
||||
}
|
||||
|
@ -291,7 +291,7 @@ func TestNaturalLanguageValues_MarshalJSON(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Errorf("Failed marshaling '%v'", err)
|
||||
}
|
||||
mRes := "{\"de\":\"test\",\"en\":\"test\"}"
|
||||
mRes := "{\"en\":\"test\",\"de\":\"test\"}"
|
||||
if string(result) != mRes {
|
||||
t.Errorf("Different results '%v' vs. '%v'", string(result), mRes)
|
||||
}
|
||||
|
@ -307,7 +307,7 @@ func TestNaturalLanguageValues_MarshalJSON(t *testing.T) {
|
|||
if err1 != nil {
|
||||
t.Errorf("Failed marshaling '%v'", err1)
|
||||
}
|
||||
mRes1 := `{"en":"test"}`
|
||||
mRes1 := `"test"`
|
||||
if string(result1) != mRes1 {
|
||||
t.Errorf("Different results '%v' vs. '%v'", string(result1), mRes1)
|
||||
}
|
||||
|
@ -343,7 +343,7 @@ func TestNaturalLanguageValues_MarshalJSON(t *testing.T) {
|
|||
if j == nil {
|
||||
t.Errorf("Error marshaling: nil value returned")
|
||||
}
|
||||
expected := fmt.Sprintf("{\"%s\":\"%s\"}", nlv.Ref, nlv.Value)
|
||||
expected := fmt.Sprintf("\"%s\"", nlv.Value)
|
||||
if string(j) != expected {
|
||||
t.Errorf("Wrong value: %s, expected %s", j, expected)
|
||||
}
|
||||
|
|
150
object.go
150
object.go
|
@ -320,35 +320,57 @@ func (o *Object) UnmarshalJSON(data []byte) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func writeProp(b *bytes.Buffer, name string, val []byte) {
|
||||
writePropName(b, name)
|
||||
writeValue(b, val)
|
||||
}
|
||||
func writePropName(b *bytes.Buffer, s string) {
|
||||
b.Write([]byte{'"'})
|
||||
b.WriteString(s)
|
||||
b.Write([]byte{'"', ':'})
|
||||
}
|
||||
|
||||
func writeValue(b *bytes.Buffer, s []byte) {
|
||||
b.Write([]byte{'"'})
|
||||
b.Write(s)
|
||||
b.Write([]byte{'"'})
|
||||
}
|
||||
|
||||
func writeNaturalLanguageProp (b *bytes.Buffer, n string, nl NaturalLanguageValues) {
|
||||
if v, err := nl.MarshalJSON(); err == nil {
|
||||
if nl.Count() > 1 {
|
||||
n += "Map"
|
||||
}
|
||||
writeProp(b, n, v)
|
||||
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 writeItemProp (b *bytes.Buffer, n string, i Item) {
|
||||
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 writeItemProp(b *bytes.Buffer, n string, i Item) (notEmpty bool) {
|
||||
notEmpty = false
|
||||
if i == nil {
|
||||
b.WriteString("nil")
|
||||
return
|
||||
return notEmpty
|
||||
}
|
||||
if i.IsObject() {
|
||||
OnObject(i, func(o *Object) error {
|
||||
|
@ -356,54 +378,68 @@ func writeItemProp (b *bytes.Buffer, n string, i Item) {
|
|||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
writeProp(b, n, v)
|
||||
notEmpty = writeProp(b, n, v)
|
||||
return nil
|
||||
})
|
||||
} else if i.IsCollection() {
|
||||
OnCollection(i, func(c CollectionInterface) error {
|
||||
writeItemCollectionProp(b, n, c.Collection())
|
||||
notEmpty = writeItemCollectionProp(b, n, c.Collection())
|
||||
return nil
|
||||
})
|
||||
}
|
||||
return notEmpty
|
||||
}
|
||||
|
||||
func writeItemCollectionProp (b *bytes.Buffer, n string, i ItemCollection) {
|
||||
func writeItemCollectionProp(b *bytes.Buffer, n string, i ItemCollection) (notEmpty bool) {
|
||||
return false
|
||||
}
|
||||
|
||||
// MarshalJSON
|
||||
func (o Object) MarshalJSON() ([]byte, error) {
|
||||
b := bytes.Buffer{}
|
||||
writeComma := func () { b.WriteString(",") }
|
||||
notEmpty := false
|
||||
b.Write([]byte{'{'})
|
||||
|
||||
if v, err := o.ID.MarshalJSON(); err == nil {
|
||||
writeProp(&b, "id", v)
|
||||
writeComma()
|
||||
writeComma := func() { b.WriteString(",") }
|
||||
writeCommaIfNotEmpty := func(notEmpty bool) {
|
||||
if notEmpty {
|
||||
writeComma()
|
||||
}
|
||||
}
|
||||
if v, err := o.Type.MarshalJSON(); err == nil {
|
||||
writeProp(&b, "type", v)
|
||||
writeComma()
|
||||
|
||||
if v, err := o.ID.MarshalJSON(); err == nil && len(v) > 0 {
|
||||
notEmpty = writeProp(&b, "id", v)
|
||||
}
|
||||
if v, err := o.MediaType.MarshalJSON(); err == nil {
|
||||
writeProp(&b, "mediaType", v)
|
||||
writeComma()
|
||||
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 {
|
||||
writeNaturalLanguageProp(&b, "name", o.Name)
|
||||
writeComma()
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeNaturalLanguageProp(&b, "name", o.Name)
|
||||
}
|
||||
if len(o.Summary) > 0 {
|
||||
writeNaturalLanguageProp(&b, "summary", o.Summary)
|
||||
writeComma()
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeNaturalLanguageProp(&b, "summary", o.Summary)
|
||||
}
|
||||
if len(o.Content) > 0 {
|
||||
writeNaturalLanguageProp(&b, "content", o.Content)
|
||||
writeComma()
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeNaturalLanguageProp(&b, "content", o.Content)
|
||||
}
|
||||
|
||||
if v, err := o.Source.MarshalJSON(); err == nil {
|
||||
writeProp(&b, "source", v)
|
||||
if v, err := o.Source.MarshalJSON(); err == nil && len(v) > 0 {
|
||||
writeCommaIfNotEmpty(notEmpty)
|
||||
notEmpty = writeProp(&b, "source", v)
|
||||
}
|
||||
return b.Bytes(), nil
|
||||
if notEmpty {
|
||||
b.Write([]byte{'}'})
|
||||
return b.Bytes(), nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Recipients performs recipient de-duplication on the Object's To, Bto, CC and BCC properties
|
||||
|
@ -578,20 +614,22 @@ func (s *Source) UnmarshalJSON(data []byte) error {
|
|||
// MarshalJSON
|
||||
func (s Source) MarshalJSON() ([]byte, error) {
|
||||
b := bytes.Buffer{}
|
||||
empty := true
|
||||
b.Write([]byte{'{'})
|
||||
if len(s.MediaType) > 0 {
|
||||
if v, err := s.MediaType.MarshalJSON(); err == nil {
|
||||
writePropName(&b, "mediaType")
|
||||
b.Write(v)
|
||||
b.Write([]byte{','})
|
||||
if v, err := s.MediaType.MarshalJSON(); err == nil && len(v) > 0 {
|
||||
empty = !writeProp(&b, "mediaType", v)
|
||||
}
|
||||
}
|
||||
if len(s.Content) > 0 {
|
||||
if v, err := s.Content.MarshalJSON(); err == nil {
|
||||
b.Write([]byte(`"content":`))
|
||||
b.Write(v)
|
||||
if !empty {
|
||||
b.Write([]byte{','})
|
||||
}
|
||||
empty = !writeNaturalLanguageProp(&b, "content", s.Content)
|
||||
}
|
||||
b.Write([]byte{'}'})
|
||||
return b.Bytes(), nil
|
||||
if !empty {
|
||||
b.Write([]byte{'}'})
|
||||
return b.Bytes(), nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
|
272
object_test.go
272
object_test.go
|
@ -3,6 +3,7 @@ package activitypub
|
|||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestObjectNew(t *testing.T) {
|
||||
|
@ -497,14 +498,269 @@ func TestObject_IsCollection(t *testing.T) {
|
|||
t.Skip("TODO")
|
||||
}
|
||||
|
||||
func TestObject_MarshalJSON(t *testing.T) {
|
||||
t.Skip("TODO")
|
||||
}
|
||||
|
||||
func TestSource_MarshalJSON(t *testing.T) {
|
||||
t.Skip("TODO")
|
||||
}
|
||||
|
||||
func TestActivityVocabularyType_MarshalJSON(t *testing.T) {
|
||||
t.Skip("TODO")
|
||||
}
|
||||
|
||||
func TestObject_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
|
||||
}
|
||||
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: "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) {
|
||||
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.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 TestSource_MarshalJSON(t *testing.T) {
|
||||
type fields struct {
|
||||
Content NaturalLanguageValues
|
||||
MediaType MimeType
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
want []byte
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Empty",
|
||||
fields: fields{},
|
||||
want: nil,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "MediaType",
|
||||
fields: fields{
|
||||
MediaType: MimeType("blank"),
|
||||
},
|
||||
want: []byte(`{"mediaType":"blank"}`),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "OneContentValue",
|
||||
fields: fields{
|
||||
Content: NaturalLanguageValues{
|
||||
{Value: "test"},
|
||||
},
|
||||
},
|
||||
want: []byte(`{"content":"test"}`),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "MultipleContentValues",
|
||||
fields: fields{
|
||||
Content: NaturalLanguageValues{
|
||||
{
|
||||
Ref: "en",
|
||||
Value: "test",
|
||||
},
|
||||
{
|
||||
Ref: "fr",
|
||||
Value: "teste",
|
||||
},
|
||||
},
|
||||
},
|
||||
want: []byte(`{"contentMap":{"en":"test","fr":"teste"}}`),
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
s := Source{
|
||||
Content: tt.fields.Content,
|
||||
MediaType: tt.fields.MediaType,
|
||||
}
|
||||
got, err := s.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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
Reference in a new issue