diff options
Diffstat (limited to 'internal/ap')
-rw-r--r-- | internal/ap/collections.go | 12 | ||||
-rw-r--r-- | internal/ap/extract.go | 47 | ||||
-rw-r--r-- | internal/ap/interfaces.go | 16 | ||||
-rw-r--r-- | internal/ap/normalize.go | 76 | ||||
-rw-r--r-- | internal/ap/util.go | 43 |
5 files changed, 121 insertions, 73 deletions
diff --git a/internal/ap/collections.go b/internal/ap/collections.go index 471dae0a1..e86d989ff 100644 --- a/internal/ap/collections.go +++ b/internal/ap/collections.go @@ -32,10 +32,10 @@ import ( func ToCollectionPageIterator(t vocab.Type) (CollectionPageIterator, error) { switch name := t.GetTypeName(); name { case ObjectCollectionPage: - t := t.(vocab.ActivityStreamsCollectionPage) //nolint:forcetypeassert + t := t.(vocab.ActivityStreamsCollectionPage) return WrapCollectionPage(t), nil case ObjectOrderedCollectionPage: - t := t.(vocab.ActivityStreamsOrderedCollectionPage) //nolint:forcetypeassert + t := t.(vocab.ActivityStreamsOrderedCollectionPage) return WrapOrderedCollectionPage(t), nil default: return nil, fmt.Errorf("%T(%s) was not CollectionPage-like", t, name) @@ -74,7 +74,7 @@ func (iter *regularCollectionPageIterator) PrevPage() WithIRI { return iter.GetActivityStreamsPrev() } -func (iter *regularCollectionPageIterator) NextItem() IteratorItemable { +func (iter *regularCollectionPageIterator) NextItem() TypeOrIRI { if !iter.initItems() { return nil } @@ -83,7 +83,7 @@ func (iter *regularCollectionPageIterator) NextItem() IteratorItemable { return cur } -func (iter *regularCollectionPageIterator) PrevItem() IteratorItemable { +func (iter *regularCollectionPageIterator) PrevItem() TypeOrIRI { if !iter.initItems() { return nil } @@ -130,7 +130,7 @@ func (iter *orderedCollectionPageIterator) PrevPage() WithIRI { return iter.GetActivityStreamsPrev() } -func (iter *orderedCollectionPageIterator) NextItem() IteratorItemable { +func (iter *orderedCollectionPageIterator) NextItem() TypeOrIRI { if !iter.initItems() { return nil } @@ -139,7 +139,7 @@ func (iter *orderedCollectionPageIterator) NextItem() IteratorItemable { return cur } -func (iter *orderedCollectionPageIterator) PrevItem() IteratorItemable { +func (iter *orderedCollectionPageIterator) PrevItem() TypeOrIRI { if !iter.initItems() { return nil } diff --git a/internal/ap/extract.go b/internal/ap/extract.go index 4cefd22dc..41cc5dcbc 100644 --- a/internal/ap/extract.go +++ b/internal/ap/extract.go @@ -35,39 +35,56 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/util" ) -// ExtractObject will extract an object vocab.Type from given implementing interface. -func ExtractObject(with WithObject) vocab.Type { +// ExtractObjects will extract object vocab.Types from given implementing interface. +func ExtractObjects(with WithObject) []TypeOrIRI { // Extract the attached object (if any). - obj := with.GetActivityStreamsObject() - if obj == nil { + objProp := with.GetActivityStreamsObject() + if objProp == nil { return nil } - // Only support single - // objects (for now...) - if obj.Len() != 1 { + // Check for zero len. + if objProp.Len() == 0 { return nil } - // Extract object vocab.Type. - return obj.At(0).GetType() + // Accumulate all of the objects into a slice. + objs := make([]TypeOrIRI, objProp.Len()) + for i := 0; i < objProp.Len(); i++ { + objs[i] = objProp.At(i) + } + + return objs } // ExtractActivityData will extract the usable data type (e.g. Note, Question, etc) and corresponding JSON, from activity. -func ExtractActivityData(activity pub.Activity, rawJSON map[string]any) (vocab.Type, map[string]any, bool) { +func ExtractActivityData(activity pub.Activity, rawJSON map[string]any) ([]TypeOrIRI, []any, bool) { switch typeName := activity.GetTypeName(); { // Activity (has "object"). case isActivity(typeName): - objType := ExtractObject(activity) - if objType == nil { + objTypes := ExtractObjects(activity) + if len(objTypes) == 0 { return nil, nil, false } - objJSON, _ := rawJSON["object"].(map[string]any) - return objType, objJSON, true + + var objJSON []any + switch json := rawJSON["object"].(type) { + case nil: + // do nothing + case map[string]any: + // Wrap map in slice. + objJSON = []any{json} + case []any: + // Use existing slice. + objJSON = json + } + + return objTypes, objJSON, true // IntransitiveAcitivity (no "object"). case isIntransitiveActivity(typeName): - return activity, rawJSON, false + asTypeOrIRI := _TypeOrIRI{activity} // wrap activity. + return []TypeOrIRI{&asTypeOrIRI}, []any{rawJSON}, true // Unknown. default: diff --git a/internal/ap/interfaces.go b/internal/ap/interfaces.go index 4538c476f..9e606db62 100644 --- a/internal/ap/interfaces.go +++ b/internal/ap/interfaces.go @@ -247,14 +247,8 @@ type CollectionPageIterator interface { NextPage() WithIRI PrevPage() WithIRI - NextItem() IteratorItemable - PrevItem() IteratorItemable -} - -// IteratorItemable represents the minimum interface for an item in an iterator. -type IteratorItemable interface { - WithIRI - WithType + NextItem() TypeOrIRI + PrevItem() TypeOrIRI } // Flaggable represents the minimum interface for an activitystreams 'Flag' activity. @@ -267,6 +261,12 @@ type Flaggable interface { WithObject } +// TypeOrIRI represents the minimum interface for something that may be a vocab.Type OR IRI. +type TypeOrIRI interface { + WithIRI + WithType +} + // WithJSONLDId represents an activity with JSONLDIdProperty. type WithJSONLDId interface { GetJSONLDId() vocab.JSONLDIdProperty diff --git a/internal/ap/normalize.go b/internal/ap/normalize.go index 52ada2848..192a2d740 100644 --- a/internal/ap/normalize.go +++ b/internal/ap/normalize.go @@ -39,60 +39,48 @@ import ( // This function is a noop if the type passed in is anything except a Create or Update with a Statusable or Accountable as its Object. func NormalizeIncomingActivity(activity pub.Activity, rawJSON map[string]interface{}) { // From the activity extract the data vocab.Type + its "raw" JSON. - dataType, rawData, ok := ExtractActivityData(activity, rawJSON) - if !ok { + dataIfaces, rawData, ok := ExtractActivityData(activity, rawJSON) + if !ok || len(dataIfaces) != len(rawData) { + // non-equal lengths *shouldn't* happen, + // but this is just an integrity check. return } - switch dataType.GetTypeName() { - // "Pollable" types. - case ActivityQuestion: - pollable, ok := dataType.(Pollable) - if !ok { - return + // Iterate over the available data. + for i, dataIface := range dataIfaces { + // Try to get as vocab.Type, else + // skip this entry for normalization. + dataType := dataIface.GetType() + if dataType == nil { + continue } - // Normalize the Pollable specific properties. - NormalizeIncomingPollOptions(pollable, rawData) - - // Fallthrough to handle - // the rest as Statusable. - fallthrough - - // "Statusable" types. - case ObjectArticle, - ObjectDocument, - ObjectImage, - ObjectVideo, - ObjectNote, - ObjectPage, - ObjectEvent, - ObjectPlace, - ObjectProfile: - statusable, ok := dataType.(Statusable) + // Get the raw data map at index, else skip + // this entry due to impossible normalization. + rawData, ok := rawData[i].(map[string]any) if !ok { - return + continue } - // Normalize everything we can on the statusable. - NormalizeIncomingContent(statusable, rawData) - NormalizeIncomingAttachments(statusable, rawData) - NormalizeIncomingSummary(statusable, rawData) - NormalizeIncomingName(statusable, rawData) - - // "Accountable" types. - case ActorApplication, - ActorGroup, - ActorOrganization, - ActorPerson, - ActorService: - accountable, ok := dataType.(Accountable) - if !ok { - return + if statusable, ok := ToStatusable(dataType); ok { + if pollable, ok := ToPollable(dataType); ok { + // Normalize the Pollable specific properties. + NormalizeIncomingPollOptions(pollable, rawData) + } + + // Normalize everything we can on the statusable. + NormalizeIncomingContent(statusable, rawData) + NormalizeIncomingAttachments(statusable, rawData) + NormalizeIncomingSummary(statusable, rawData) + NormalizeIncomingName(statusable, rawData) + continue } - // Normalize everything we can on the accountable. - NormalizeIncomingSummary(accountable, rawData) + if accountable, ok := ToAccountable(dataType); ok { + // Normalize everything we can on the accountable. + NormalizeIncomingSummary(accountable, rawData) + continue + } } } diff --git a/internal/ap/util.go b/internal/ap/util.go new file mode 100644 index 000000000..c810b7985 --- /dev/null +++ b/internal/ap/util.go @@ -0,0 +1,43 @@ +// GoToSocial +// Copyright (C) GoToSocial Authors admin@gotosocial.org +// SPDX-License-Identifier: AGPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. + +package ap + +import ( + "net/url" + + "github.com/superseriousbusiness/activity/streams/vocab" +) + +// _TypeOrIRI wraps a vocab.Type to implement TypeOrIRI. +type _TypeOrIRI struct { + vocab.Type +} + +func (t *_TypeOrIRI) GetType() vocab.Type { + return t.Type +} + +func (t *_TypeOrIRI) GetIRI() *url.URL { + return nil +} + +func (t *_TypeOrIRI) IsIRI() bool { + return false +} + +func (t *_TypeOrIRI) SetIRI(*url.URL) {} |