diff options
Diffstat (limited to 'internal/ap/serialize.go')
-rw-r--r-- | internal/ap/serialize.go | 139 |
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 |