mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2025-11-18 09:37:28 -06:00
[feature] Federate pinned posts (aka featuredCollection) in and out (#1560)
* start fiddling * the ol' fiddle + update * start working on fetching statuses * poopy doopy doo where r u uwu * further adventures in featuring statuses * finishing up * fmt * simply status unpin loop * move empty featured check back to caller function * remove unnecessary log.WithContext calls * remove unnecessary IsIRI() checks * add explanatory comment about status URIs * change log level to error * better test names
This commit is contained in:
parent
87c5c42972
commit
24cec4e7aa
29 changed files with 783 additions and 278 deletions
|
|
@ -181,9 +181,14 @@ func (c *converter) ASRepresentationToAccount(ctx context.Context, accountable a
|
|||
acct.FollowersURI = accountable.GetActivityStreamsFollowers().GetIRI().String()
|
||||
}
|
||||
|
||||
// FeaturedURI
|
||||
if accountable.GetTootFeatured() != nil && accountable.GetTootFeatured().GetIRI() != nil {
|
||||
acct.FeaturedCollectionURI = accountable.GetTootFeatured().GetIRI().String()
|
||||
// FeaturedURI aka pinned collection:
|
||||
// Only trust featured URI if it has at least two domains,
|
||||
// from the right, in common with the domain of the account
|
||||
if featured := accountable.GetTootFeatured(); featured != nil && featured.IsIRI() {
|
||||
if featuredURI := featured.GetIRI(); // nocollapse
|
||||
featuredURI != nil && dns.CompareDomainName(acct.Domain, featuredURI.Host) >= 2 {
|
||||
acct.FeaturedCollectionURI = featuredURI.String()
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: FeaturedTagsURI
|
||||
|
|
|
|||
|
|
@ -178,6 +178,9 @@ type TypeConverter interface {
|
|||
//
|
||||
// Appropriate 'next' and 'prev' fields will be created based on the highest and lowest IDs present in the statuses slice.
|
||||
StatusesToASOutboxPage(ctx context.Context, outboxID string, maxID string, minID string, statuses []*gtsmodel.Status) (vocab.ActivityStreamsOrderedCollectionPage, error)
|
||||
// StatusesToASFeaturedCollection converts a slice of statuses into an ordered collection
|
||||
// of URIs, suitable for serializing and serving via the activitypub API.
|
||||
StatusesToASFeaturedCollection(ctx context.Context, featuredCollectionID string, statuses []*gtsmodel.Status) (vocab.ActivityStreamsOrderedCollection, error)
|
||||
// ReportToASFlag converts a gts model report into an activitystreams FLAG, suitable for federation.
|
||||
ReportToASFlag(ctx context.Context, r *gtsmodel.Report) (vocab.ActivityStreamsFlag, error)
|
||||
|
||||
|
|
|
|||
|
|
@ -1296,6 +1296,34 @@ func (c *converter) OutboxToASCollection(ctx context.Context, outboxID string) (
|
|||
return collection, nil
|
||||
}
|
||||
|
||||
func (c *converter) StatusesToASFeaturedCollection(ctx context.Context, featuredCollectionID string, statuses []*gtsmodel.Status) (vocab.ActivityStreamsOrderedCollection, error) {
|
||||
collection := streams.NewActivityStreamsOrderedCollection()
|
||||
|
||||
collectionIDProp := streams.NewJSONLDIdProperty()
|
||||
featuredCollectionIDURI, err := url.Parse(featuredCollectionID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing url %s", featuredCollectionID)
|
||||
}
|
||||
collectionIDProp.SetIRI(featuredCollectionIDURI)
|
||||
collection.SetJSONLDId(collectionIDProp)
|
||||
|
||||
itemsProp := streams.NewActivityStreamsOrderedItemsProperty()
|
||||
for _, s := range statuses {
|
||||
uri, err := url.Parse(s.URI)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing url %s", s.URI)
|
||||
}
|
||||
itemsProp.AppendIRI(uri)
|
||||
}
|
||||
collection.SetActivityStreamsOrderedItems(itemsProp)
|
||||
|
||||
totalItemsProp := streams.NewActivityStreamsTotalItemsProperty()
|
||||
totalItemsProp.Set(len(statuses))
|
||||
collection.SetActivityStreamsTotalItems(totalItemsProp)
|
||||
|
||||
return collection, nil
|
||||
}
|
||||
|
||||
func (c *converter) ReportToASFlag(ctx context.Context, r *gtsmodel.Report) (vocab.ActivityStreamsFlag, error) {
|
||||
flag := streams.NewActivityStreamsFlag()
|
||||
|
||||
|
|
|
|||
|
|
@ -21,11 +21,13 @@ package typeutils_test
|
|||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/suite"
|
||||
"github.com/superseriousbusiness/activity/streams"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||
)
|
||||
|
|
@ -544,6 +546,96 @@ func (suite *InternalToASTestSuite) TestReportToAS() {
|
|||
}`, string(bytes))
|
||||
}
|
||||
|
||||
func (suite *InternalToASTestSuite) TestPinnedStatusesToASSomeItems() {
|
||||
ctx := context.Background()
|
||||
|
||||
testAccount := suite.testAccounts["admin_account"]
|
||||
statuses, err := suite.db.GetAccountPinnedStatuses(ctx, testAccount.ID)
|
||||
if err != nil {
|
||||
suite.FailNow(err.Error())
|
||||
}
|
||||
|
||||
collection, err := suite.typeconverter.StatusesToASFeaturedCollection(ctx, testAccount.FeaturedCollectionURI, statuses)
|
||||
if err != nil {
|
||||
suite.FailNow(err.Error())
|
||||
}
|
||||
|
||||
ser, err := streams.Serialize(collection)
|
||||
suite.NoError(err)
|
||||
|
||||
bytes, err := json.MarshalIndent(ser, "", " ")
|
||||
suite.NoError(err)
|
||||
|
||||
suite.Equal(`{
|
||||
"@context": "https://www.w3.org/ns/activitystreams",
|
||||
"id": "http://localhost:8080/users/admin/collections/featured",
|
||||
"orderedItems": [
|
||||
"http://localhost:8080/users/admin/statuses/01F8MHAAY43M6RJ473VQFCVH37",
|
||||
"http://localhost:8080/users/admin/statuses/01F8MH75CBF9JFX4ZAD54N0W0R"
|
||||
],
|
||||
"totalItems": 2,
|
||||
"type": "OrderedCollection"
|
||||
}`, string(bytes))
|
||||
}
|
||||
|
||||
func (suite *InternalToASTestSuite) TestPinnedStatusesToASNoItems() {
|
||||
ctx := context.Background()
|
||||
|
||||
testAccount := suite.testAccounts["local_account_1"]
|
||||
statuses, err := suite.db.GetAccountPinnedStatuses(ctx, testAccount.ID)
|
||||
if err != nil && !errors.Is(err, db.ErrNoEntries) {
|
||||
suite.FailNow(err.Error())
|
||||
}
|
||||
|
||||
collection, err := suite.typeconverter.StatusesToASFeaturedCollection(ctx, testAccount.FeaturedCollectionURI, statuses)
|
||||
if err != nil {
|
||||
suite.FailNow(err.Error())
|
||||
}
|
||||
|
||||
ser, err := streams.Serialize(collection)
|
||||
suite.NoError(err)
|
||||
|
||||
bytes, err := json.MarshalIndent(ser, "", " ")
|
||||
suite.NoError(err)
|
||||
|
||||
suite.Equal(`{
|
||||
"@context": "https://www.w3.org/ns/activitystreams",
|
||||
"id": "http://localhost:8080/users/the_mighty_zork/collections/featured",
|
||||
"orderedItems": [],
|
||||
"totalItems": 0,
|
||||
"type": "OrderedCollection"
|
||||
}`, string(bytes))
|
||||
}
|
||||
|
||||
func (suite *InternalToASTestSuite) TestPinnedStatusesToASOneItem() {
|
||||
ctx := context.Background()
|
||||
|
||||
testAccount := suite.testAccounts["local_account_2"]
|
||||
statuses, err := suite.db.GetAccountPinnedStatuses(ctx, testAccount.ID)
|
||||
if err != nil {
|
||||
suite.FailNow(err.Error())
|
||||
}
|
||||
|
||||
collection, err := suite.typeconverter.StatusesToASFeaturedCollection(ctx, testAccount.FeaturedCollectionURI, statuses)
|
||||
if err != nil {
|
||||
suite.FailNow(err.Error())
|
||||
}
|
||||
|
||||
ser, err := streams.Serialize(collection)
|
||||
suite.NoError(err)
|
||||
|
||||
bytes, err := json.MarshalIndent(ser, "", " ")
|
||||
suite.NoError(err)
|
||||
|
||||
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",
|
||||
"totalItems": 1,
|
||||
"type": "OrderedCollection"
|
||||
}`, string(bytes))
|
||||
}
|
||||
|
||||
func TestInternalToASTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(InternalToASTestSuite))
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue