summaryrefslogtreecommitdiff
path: root/internal/ap/serialize.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/ap/serialize.go')
-rw-r--r--internal/ap/serialize.go139
1 files changed, 58 insertions, 81 deletions
diff --git a/internal/ap/serialize.go b/internal/ap/serialize.go
index 368d7f9a2..944e67407 100644
--- a/internal/ap/serialize.go
+++ b/internal/ap/serialize.go
@@ -18,10 +18,9 @@
package ap
import (
- "fmt"
-
"github.com/superseriousbusiness/activity/streams"
"github.com/superseriousbusiness/activity/streams/vocab"
+ "github.com/superseriousbusiness/gotosocial/internal/gtserror"
)
// Serialize is a custom serializer for ActivityStreams types.
@@ -35,17 +34,20 @@ import (
//
// Currently, the following things will be custom serialized:
//
-// - OrderedCollection: 'orderedItems' property will always be made into an array.
-// - Any Accountable type: 'attachment' property will always be made into an array.
-// - Update: any Accountable 'object's set on an update will be custom serialized as above.
+// - OrderedCollection: 'orderedItems' property will always be made into an array.
+// - Any Accountable type: 'attachment' property will always be made into an array.
+// - Any Statusable type: 'attachment' property will always be made into an array; 'content' and 'contentMap' will be normalized.
+// - Any Activityable type: any 'object's set on an activity will be custom serialized as above.
func Serialize(t vocab.Type) (m map[string]interface{}, e error) {
- switch t.GetTypeName() {
- case ObjectOrderedCollection:
+ switch tn := t.GetTypeName(); {
+ case tn == ObjectOrderedCollection:
return serializeOrderedCollection(t)
- case ActorApplication, ActorGroup, ActorOrganization, ActorPerson, ActorService:
+ case IsAccountable(tn):
return serializeAccountable(t, true)
- case ActivityUpdate:
- return serializeWithObject(t)
+ case IsStatusable(tn):
+ return serializeStatusable(t, true)
+ case IsActivityable(tn):
+ return serializeActivityable(t, true)
default:
// No custom serializer necessary.
return streams.Serialize(t)
@@ -61,8 +63,8 @@ func Serialize(t vocab.Type) (m map[string]interface{}, e error) {
// See:
// - https://github.com/go-fed/activity/issues/139
// - https://github.com/mastodon/mastodon/issues/24225
-func serializeOrderedCollection(orderedCollection vocab.Type) (map[string]interface{}, error) {
- data, err := streams.Serialize(orderedCollection)
+func serializeOrderedCollection(t vocab.Type) (map[string]interface{}, error) {
+ data, err := streams.Serialize(t)
if err != nil {
return nil, err
}
@@ -99,7 +101,12 @@ func serializeOrderedCollection(orderedCollection vocab.Type) (map[string]interf
// If the accountable is being serialized as part of another object (eg., as the
// object of an activity), then includeContext should be set to false, as the
// @context entry should be included on the top-level/wrapping activity/object.
-func serializeAccountable(accountable vocab.Type, includeContext bool) (map[string]interface{}, error) {
+func serializeAccountable(t vocab.Type, includeContext bool) (map[string]interface{}, error) {
+ accountable, ok := t.(Accountable)
+ if !ok {
+ return nil, gtserror.Newf("vocab.Type %T not accountable", t)
+ }
+
var (
data map[string]interface{}
err error
@@ -115,91 +122,61 @@ func serializeAccountable(accountable vocab.Type, includeContext bool) (map[stri
return nil, err
}
- attachment, ok := data["attachment"]
- if !ok {
- // No 'attachment', nothing to change.
- return data, nil
- }
-
- if _, ok := attachment.([]interface{}); ok {
- // Already slice.
- return data, nil
- }
-
- // Coerce single-object to slice.
- data["attachment"] = []interface{}{attachment}
+ NormalizeOutgoingAttachmentProp(accountable, data)
return data, nil
}
-func serializeWithObject(t vocab.Type) (map[string]interface{}, error) {
- withObject, ok := t.(WithObject)
+func serializeStatusable(t vocab.Type, includeContext bool) (map[string]interface{}, error) {
+ statusable, ok := t.(Statusable)
if !ok {
- return nil, fmt.Errorf("serializeWithObject: could not resolve %T to WithObject", t)
+ return nil, gtserror.Newf("vocab.Type %T not statusable", t)
+ }
+
+ var (
+ data map[string]interface{}
+ err error
+ )
+
+ if includeContext {
+ data, err = streams.Serialize(statusable)
+ } else {
+ data, err = statusable.Serialize()
}
- data, err := streams.Serialize(t)
if err != nil {
return nil, err
}
- object := withObject.GetActivityStreamsObject()
- if object == nil {
- // Nothing to do, bail early.
- return data, nil
+ NormalizeOutgoingAttachmentProp(statusable, data)
+ NormalizeOutgoingContentProp(statusable, data)
+
+ return data, nil
+}
+
+func serializeActivityable(t vocab.Type, includeContext bool) (map[string]interface{}, error) {
+ activityable, ok := t.(Activityable)
+ if !ok {
+ return nil, gtserror.Newf("vocab.Type %T not activityable", t)
}
- objectLen := object.Len()
- if objectLen == 0 {
- // Nothing to do, bail early.
- return data, nil
+ var (
+ data map[string]interface{}
+ err error
+ )
+
+ if includeContext {
+ data, err = streams.Serialize(activityable)
+ } else {
+ data, err = activityable.Serialize()
}
- // The thing we already serialized has objects
- // on it, so we should see if we need to custom
- // serialize any of those objects, and replace
- // them on the data map as necessary.
- objects := make([]interface{}, 0, objectLen)
- for iter := object.Begin(); iter != object.End(); iter = iter.Next() {
- if iter.IsIRI() {
- // Plain IRIs don't need custom serialization.
- objects = append(objects, iter.GetIRI().String())
- continue
- }
-
- var (
- objectType = iter.GetType()
- objectSer map[string]interface{}
- )
-
- if objectType == nil {
- // This is awkward.
- return nil, fmt.Errorf("serializeWithObject: could not resolve object iter %T to vocab.Type", iter)
- }
-
- switch objectType.GetTypeName() {
- case ActorApplication, ActorGroup, ActorOrganization, ActorPerson, ActorService:
- // @context will be included in wrapping type already,
- // we don't need to include it in the object itself.
- objectSer, err = serializeAccountable(objectType, false)
- default:
- // No custom serializer for this type; serialize as normal.
- objectSer, err = objectType.Serialize()
- }
-
- if err != nil {
- return nil, err
- }
-
- objects = append(objects, objectSer)
+ if err != nil {
+ return nil, err
}
- if objectLen == 1 {
- // Unnest single object.
- data["object"] = objects[0]
- } else {
- // Array of objects.
- data["object"] = objects
+ if err := NormalizeOutgoingObjectProp(activityable, data); err != nil {
+ return nil, err
}
return data, nil