commit 4f322f527f6c6e043f9efa13034fed1dbd35f1eb
parent 8d2a76c58ce018fb6cd6760a3607cf1ee720037a
Author: tobi <31960611+tsmethurst@users.noreply.github.com>
Date: Thu, 6 Apr 2023 13:16:53 +0200
[bugfix] Always serialize orderedItems as array (#1673)
Diffstat:
4 files changed, 77 insertions(+), 7 deletions(-)
diff --git a/docs/federation/federating_with_gotosocial.md b/docs/federation/federating_with_gotosocial.md
@@ -236,13 +236,15 @@ Example of a featured collection of a user who has pinned multiple `Note`s:
}
```
-Example of a user who has pinned one `Note` (`orderedItems` is just a URL string now!):
+Example of a user who has pinned one `Note`:
```json
{
"@context": "https://www.w3.org/ns/activitystreams",
"id": "https://example.org/users/some_user/collections/featured",
- "orderedItems": "https://example.org/users/some_user/statuses/01GS7VTYH0S77NNXTP6W4G9EAG",
+ "orderedItems": [
+ "https://example.org/users/some_user/statuses/01GS7VTYH0S77NNXTP6W4G9EAG"
+ ],
"totalItems": 1,
"type": "OrderedCollection"
}
diff --git a/internal/ap/serialize.go b/internal/ap/serialize.go
@@ -0,0 +1,64 @@
+// 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 (
+ "errors"
+
+ "github.com/superseriousbusiness/activity/streams"
+ "github.com/superseriousbusiness/activity/streams/vocab"
+)
+
+// SerializeOrderedCollection is a custom serializer for an ActivityStreamsOrderedCollection.
+// Unlike the standard streams.Serialize function, this serializer normalizes the orderedItems
+// value to always be an array/slice, regardless of how many items are contained therein.
+//
+// TODO: Remove this function if we can fix the underlying issue in Go-Fed.
+//
+// See:
+// - https://github.com/go-fed/activity/issues/139
+// - https://github.com/mastodon/mastodon/issues/24225
+func SerializeOrderedCollection(orderedCollection vocab.ActivityStreamsOrderedCollection) (map[string]interface{}, error) {
+ data, err := streams.Serialize(orderedCollection)
+ if err != nil {
+ return nil, err
+ }
+
+ return data, normalizeOrderedCollectionData(data)
+}
+
+func normalizeOrderedCollectionData(rawOrderedCollection map[string]interface{}) error {
+ orderedItems, ok := rawOrderedCollection["orderedItems"]
+ if !ok {
+ return errors.New("no orderedItems set on OrderedCollection")
+ }
+
+ if _, ok := orderedItems.([]interface{}); ok {
+ // Already slice.
+ return nil
+ }
+
+ orderedItemsString, ok := orderedItems.(string)
+ if !ok {
+ return errors.New("orderedItems was neither slice nor string")
+ }
+
+ rawOrderedCollection["orderedItems"] = []string{orderedItemsString}
+
+ return nil
+}
diff --git a/internal/processing/fedi/collections.go b/internal/processing/fedi/collections.go
@@ -25,6 +25,7 @@ import (
"net/url"
"github.com/superseriousbusiness/activity/streams"
+ "github.com/superseriousbusiness/gotosocial/internal/ap"
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
)
@@ -172,7 +173,7 @@ func (p *Processor) FeaturedCollectionGet(ctx context.Context, requestedUsername
return nil, gtserror.NewErrorInternalError(err)
}
- data, err := streams.Serialize(collection)
+ data, err := ap.SerializeOrderedCollection(collection)
if err != nil {
return nil, gtserror.NewErrorInternalError(err)
}
diff --git a/internal/typeutils/internaltoas_test.go b/internal/typeutils/internaltoas_test.go
@@ -26,6 +26,7 @@ import (
"github.com/stretchr/testify/suite"
"github.com/superseriousbusiness/activity/streams"
+ "github.com/superseriousbusiness/gotosocial/internal/ap"
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/testrig"
@@ -669,7 +670,7 @@ func (suite *InternalToASTestSuite) TestPinnedStatusesToASSomeItems() {
suite.FailNow(err.Error())
}
- ser, err := streams.Serialize(collection)
+ ser, err := ap.SerializeOrderedCollection(collection)
suite.NoError(err)
bytes, err := json.MarshalIndent(ser, "", " ")
@@ -701,7 +702,7 @@ func (suite *InternalToASTestSuite) TestPinnedStatusesToASNoItems() {
suite.FailNow(err.Error())
}
- ser, err := streams.Serialize(collection)
+ ser, err := ap.SerializeOrderedCollection(collection)
suite.NoError(err)
bytes, err := json.MarshalIndent(ser, "", " ")
@@ -730,7 +731,7 @@ func (suite *InternalToASTestSuite) TestPinnedStatusesToASOneItem() {
suite.FailNow(err.Error())
}
- ser, err := streams.Serialize(collection)
+ ser, err := ap.SerializeOrderedCollection(collection)
suite.NoError(err)
bytes, err := json.MarshalIndent(ser, "", " ")
@@ -739,7 +740,9 @@ func (suite *InternalToASTestSuite) TestPinnedStatusesToASOneItem() {
suite.Equal(`{
"@context": "https://www.w3.org/ns/activitystreams",
"id": "http://localhost:8080/users/1happyturtle/collections/featured",
- "orderedItems": "http://localhost:8080/users/1happyturtle/statuses/01G20ZM733MGN8J344T4ZDDFY1",
+ "orderedItems": [
+ "http://localhost:8080/users/1happyturtle/statuses/01G20ZM733MGN8J344T4ZDDFY1"
+ ],
"totalItems": 1,
"type": "OrderedCollection"
}`, string(bytes))