summaryrefslogtreecommitdiff
path: root/internal/ap
diff options
context:
space:
mode:
Diffstat (limited to 'internal/ap')
-rw-r--r--internal/ap/collections.go12
-rw-r--r--internal/ap/extract.go47
-rw-r--r--internal/ap/interfaces.go16
-rw-r--r--internal/ap/normalize.go76
-rw-r--r--internal/ap/util.go43
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) {}