From f0c9f4169be6c5c7dd913f348cdd294a19038d63 Mon Sep 17 00:00:00 2001
From: tobi <31960611+tsmethurst@users.noreply.github.com>
Date: Mon, 23 May 2022 17:40:03 +0200
Subject: [PATCH] [bugfix] Fix multiple dereferences of boosted status causing
media duplication (#589)
* add some announces to test models
* start on announce test logic
* test federatingDB.Announce
* change signature of GetRemoteStatus
* remove 'refresh' logic and replace it with refetch
* go fmt
* remove timeline manager from processor test
* make zork created at determinate
* test get account statuses
* test get + serialize zork
* make account keys determinate
* make admin accountCreate time determinate
* test account to as
* init test config before test log
* test status to frontend
* remove daft Within check
* hack around a bit
* use index of slice
---
.../api/client/account/accountverify_test.go | 3 -
internal/db/bundb/account_test.go | 6 +
internal/federation/dereference.go | 4 +-
internal/federation/dereferencing/announce.go | 2 +-
.../federation/dereferencing/dereferencer.go | 2 +-
internal/federation/dereferencing/status.go | 87 +++++--------
.../federation/dereferencing/status_test.go | 12 +-
internal/federation/dereferencing/thread.go | 13 +-
.../federation/federatingdb/announce_test.go | 93 ++++++++++++++
.../federatingdb/federatingdb_test.go | 3 +-
internal/federation/federator.go | 2 +-
internal/processing/fromfederator.go | 2 +-
internal/processing/processor_test.go | 2 -
internal/processing/search.go | 2 +-
internal/typeutils/converter_test.go | 2 +-
internal/typeutils/internaltoas_test.go | 9 +-
internal/typeutils/internaltofrontend_test.go | 58 +++++++++
testrig/testmodels.go | 121 ++++++++++++++++--
18 files changed, 324 insertions(+), 99 deletions(-)
create mode 100644 internal/federation/federatingdb/announce_test.go
create mode 100644 internal/typeutils/internaltofrontend_test.go
diff --git a/internal/api/client/account/accountverify_test.go b/internal/api/client/account/accountverify_test.go
index 4d6820881..6b199d83e 100644
--- a/internal/api/client/account/accountverify_test.go
+++ b/internal/api/client/account/accountverify_test.go
@@ -65,8 +65,6 @@ func (suite *AccountVerifyTestSuite) TestAccountVerifyGet() {
createdAt, err := time.Parse(time.RFC3339, apimodelAccount.CreatedAt)
suite.NoError(err)
- lastStatusAt, err := time.Parse(time.RFC3339, apimodelAccount.LastStatusAt)
- suite.NoError(err)
suite.Equal(testAccount.ID, apimodelAccount.ID)
suite.Equal(testAccount.Username, apimodelAccount.Username)
@@ -83,7 +81,6 @@ func (suite *AccountVerifyTestSuite) TestAccountVerifyGet() {
suite.Equal(2, apimodelAccount.FollowersCount)
suite.Equal(2, apimodelAccount.FollowingCount)
suite.Equal(5, apimodelAccount.StatusesCount)
- suite.WithinDuration(time.Now(), lastStatusAt, 5*time.Minute)
suite.EqualValues(gtsmodel.VisibilityPublic, apimodelAccount.Source.Privacy)
suite.Equal(testAccount.Language, apimodelAccount.Source.Language)
suite.Equal(testAccount.NoteRaw, apimodelAccount.Source.Note)
diff --git a/internal/db/bundb/account_test.go b/internal/db/bundb/account_test.go
index 90824416d..33c6b7ee6 100644
--- a/internal/db/bundb/account_test.go
+++ b/internal/db/bundb/account_test.go
@@ -34,6 +34,12 @@ type AccountTestSuite struct {
BunDBStandardTestSuite
}
+func (suite *AccountTestSuite) TestGetAccountStatuses() {
+ statuses, err := suite.db.GetAccountStatuses(context.Background(), suite.testAccounts["local_account_1"].ID, 20, false, false, "", "", false, false, false)
+ suite.NoError(err)
+ suite.Len(statuses, 5)
+}
+
func (suite *AccountTestSuite) TestGetAccountByIDWithExtras() {
account, err := suite.db.GetAccountByID(context.Background(), suite.testAccounts["local_account_1"].ID)
if err != nil {
diff --git a/internal/federation/dereference.go b/internal/federation/dereference.go
index 8cb23a91f..8efa0cc7e 100644
--- a/internal/federation/dereference.go
+++ b/internal/federation/dereference.go
@@ -30,8 +30,8 @@ func (f *federator) GetRemoteAccount(ctx context.Context, username string, remot
return f.dereferencer.GetRemoteAccount(ctx, username, remoteAccountID, blocking, refresh)
}
-func (f *federator) GetRemoteStatus(ctx context.Context, username string, remoteStatusID *url.URL, refresh, includeParent bool) (*gtsmodel.Status, ap.Statusable, bool, error) {
- return f.dereferencer.GetRemoteStatus(ctx, username, remoteStatusID, refresh, includeParent)
+func (f *federator) GetRemoteStatus(ctx context.Context, username string, remoteStatusID *url.URL, refetch, includeParent bool) (*gtsmodel.Status, ap.Statusable, error) {
+ return f.dereferencer.GetRemoteStatus(ctx, username, remoteStatusID, refetch, includeParent)
}
func (f *federator) EnrichRemoteStatus(ctx context.Context, username string, status *gtsmodel.Status, includeParent bool) (*gtsmodel.Status, error) {
diff --git a/internal/federation/dereferencing/announce.go b/internal/federation/dereferencing/announce.go
index 7bde7d2ce..c740bb20a 100644
--- a/internal/federation/dereferencing/announce.go
+++ b/internal/federation/dereferencing/announce.go
@@ -46,7 +46,7 @@ func (d *deref) DereferenceAnnounce(ctx context.Context, announce *gtsmodel.Stat
return fmt.Errorf("DereferenceAnnounce: error dereferencing thread of boosted status: %s", err)
}
- boostedStatus, _, _, err := d.GetRemoteStatus(ctx, requestingUsername, boostedStatusURI, false, true)
+ boostedStatus, _, err := d.GetRemoteStatus(ctx, requestingUsername, boostedStatusURI, false, true)
if err != nil {
return fmt.Errorf("DereferenceAnnounce: error dereferencing remote status with id %s: %s", announce.BoostOf.URI, err)
}
diff --git a/internal/federation/dereferencing/dereferencer.go b/internal/federation/dereferencing/dereferencer.go
index 855c4baf8..cae24d0fd 100644
--- a/internal/federation/dereferencing/dereferencer.go
+++ b/internal/federation/dereferencing/dereferencer.go
@@ -35,7 +35,7 @@
type Dereferencer interface {
GetRemoteAccount(ctx context.Context, username string, remoteAccountID *url.URL, blocking bool, refresh bool) (*gtsmodel.Account, error)
- GetRemoteStatus(ctx context.Context, username string, remoteStatusID *url.URL, refresh, includeParent bool) (*gtsmodel.Status, ap.Statusable, bool, error)
+ GetRemoteStatus(ctx context.Context, username string, remoteStatusID *url.URL, refetch, includeParent bool) (*gtsmodel.Status, ap.Statusable, error)
EnrichRemoteStatus(ctx context.Context, username string, status *gtsmodel.Status, includeParent bool) (*gtsmodel.Status, error)
GetRemoteInstance(ctx context.Context, username string, remoteInstanceURI *url.URL) (*gtsmodel.Instance, error)
diff --git a/internal/federation/dereferencing/status.go b/internal/federation/dereferencing/status.go
index cacca91b2..7c4d588bb 100644
--- a/internal/federation/dereferencing/status.go
+++ b/internal/federation/dereferencing/status.go
@@ -53,79 +53,63 @@ func (d *deref) EnrichRemoteStatus(ctx context.Context, username string, status
}
// GetRemoteStatus completely dereferences a remote status, converts it to a GtS model status,
-// puts it in the database, and returns it to a caller. The boolean indicates whether the status is new
-// to us or not. If we haven't seen the status before, bool will be true. If we have seen the status before,
-// it will be false.
+// puts it in the database, and returns it to a caller.
//
-// If refresh is true, then even if we have the status in our database already, it will be dereferenced from its
-// remote representation, as will its owner.
+// If refetch is true, then regardless of whether we have the original status in the database or not,
+// the ap.Statusable representation of the status will be dereferenced and returned.
//
-// If a dereference was performed, then the function also returns the ap.Statusable representation for further processing.
+// If refetch is false, the ap.Statusable will only be returned if this is a new status, so callers
+// should check whether or not this is nil.
//
// SIDE EFFECTS: remote status will be stored in the database, and the remote status owner will also be stored.
-func (d *deref) GetRemoteStatus(ctx context.Context, username string, remoteStatusID *url.URL, refresh, includeParent bool) (*gtsmodel.Status, ap.Statusable, bool, error) {
- new := true
-
- // check if we already have the status in our db
+func (d *deref) GetRemoteStatus(ctx context.Context, username string, remoteStatusID *url.URL, refetch, includeParent bool) (*gtsmodel.Status, ap.Statusable, error) {
maybeStatus, err := d.db.GetStatusByURI(ctx, remoteStatusID.String())
- if err == nil {
- // we've seen this status before so it's not new
- new = false
-
- // if we're not being asked to refresh, we can just return the maybeStatus as-is and avoid doing any external calls
- if !refresh {
- return maybeStatus, nil, new, nil
- }
+ if err == nil && !refetch {
+ // we already had the status and we aren't being asked to refetch the AP representation
+ return maybeStatus, nil, nil
}
statusable, err := d.dereferenceStatusable(ctx, username, remoteStatusID)
if err != nil {
- return nil, statusable, new, fmt.Errorf("GetRemoteStatus: error dereferencing statusable: %s", err)
+ return nil, nil, fmt.Errorf("GetRemoteStatus: error dereferencing statusable: %s", err)
}
+ if maybeStatus != nil && refetch {
+ // we already had the status and we've successfully fetched the AP representation as requested
+ return maybeStatus, statusable, nil
+ }
+
+ // from here on out we can consider this to be a 'new' status because we didn't have the status in the db already
accountURI, err := ap.ExtractAttributedTo(statusable)
if err != nil {
- return nil, statusable, new, fmt.Errorf("GetRemoteStatus: error extracting attributedTo: %s", err)
+ return nil, nil, fmt.Errorf("GetRemoteStatus: error extracting attributedTo: %s", err)
}
- // do this so we know we have the remote account of the status in the db
_, err = d.GetRemoteAccount(ctx, username, accountURI, true, false)
if err != nil {
- return nil, statusable, new, fmt.Errorf("GetRemoteStatus: couldn't derive status author: %s", err)
+ return nil, nil, fmt.Errorf("GetRemoteStatus: couldn't get status author: %s", err)
}
gtsStatus, err := d.typeConverter.ASStatusToStatus(ctx, statusable)
if err != nil {
- return nil, statusable, new, fmt.Errorf("GetRemoteStatus: error converting statusable to status: %s", err)
+ return nil, statusable, fmt.Errorf("GetRemoteStatus: error converting statusable to status: %s", err)
}
- if new {
- ulid, err := id.NewULIDFromTime(gtsStatus.CreatedAt)
- if err != nil {
- return nil, statusable, new, fmt.Errorf("GetRemoteStatus: error generating new id for status: %s", err)
- }
- gtsStatus.ID = ulid
+ ulid, err := id.NewULIDFromTime(gtsStatus.CreatedAt)
+ if err != nil {
+ return nil, nil, fmt.Errorf("GetRemoteStatus: error generating new id for status: %s", err)
+ }
+ gtsStatus.ID = ulid
- if err := d.populateStatusFields(ctx, gtsStatus, username, includeParent); err != nil {
- return nil, statusable, new, fmt.Errorf("GetRemoteStatus: error populating status fields: %s", err)
- }
-
- if err := d.db.PutStatus(ctx, gtsStatus); err != nil {
- return nil, statusable, new, fmt.Errorf("GetRemoteStatus: error putting new status: %s", err)
- }
- } else {
- gtsStatus.ID = maybeStatus.ID
-
- if err := d.populateStatusFields(ctx, gtsStatus, username, includeParent); err != nil {
- return nil, statusable, new, fmt.Errorf("GetRemoteStatus: error populating status fields: %s", err)
- }
-
- if err := d.db.UpdateByPrimaryKey(ctx, gtsStatus); err != nil {
- return nil, statusable, new, fmt.Errorf("GetRemoteStatus: error updating status: %s", err)
- }
+ if err := d.populateStatusFields(ctx, gtsStatus, username, includeParent); err != nil {
+ return nil, nil, fmt.Errorf("GetRemoteStatus: error populating status fields: %s", err)
}
- return gtsStatus, statusable, new, nil
+ if err := d.db.PutStatus(ctx, gtsStatus); err != nil {
+ return nil, nil, fmt.Errorf("GetRemoteStatus: error putting new status: %s", err)
+ }
+
+ return gtsStatus, statusable, nil
}
func (d *deref) dereferenceStatusable(ctx context.Context, username string, remoteStatusID *url.URL) (ap.Statusable, error) {
@@ -429,14 +413,9 @@ func (d *deref) populateStatusRepliedTo(ctx context.Context, status *gtsmodel.St
return err
}
- // see if we have the status in our db already
- replyToStatus, err := d.db.GetStatusByURI(ctx, status.InReplyToURI)
+ replyToStatus, _, err := d.GetRemoteStatus(ctx, requestingUsername, statusURI, false, false)
if err != nil {
- // Status was not in the DB, try fetch
- replyToStatus, _, _, err = d.GetRemoteStatus(ctx, requestingUsername, statusURI, false, false)
- if err != nil {
- return fmt.Errorf("populateStatusRepliedTo: couldn't get reply to status with uri %s: %s", status.InReplyToURI, err)
- }
+ return fmt.Errorf("populateStatusRepliedTo: couldn't get reply to status with uri %s: %s", status.InReplyToURI, err)
}
// we have the status
diff --git a/internal/federation/dereferencing/status_test.go b/internal/federation/dereferencing/status_test.go
index 08363f580..ab6efad38 100644
--- a/internal/federation/dereferencing/status_test.go
+++ b/internal/federation/dereferencing/status_test.go
@@ -38,11 +38,9 @@ func (suite *StatusTestSuite) TestDereferenceSimpleStatus() {
fetchingAccount := suite.testAccounts["local_account_1"]
statusURL := testrig.URLMustParse("https://unknown-instance.com/users/brand_new_person/statuses/01FE4NTHKWW7THT67EF10EB839")
- status, statusable, new, err := suite.dereferencer.GetRemoteStatus(context.Background(), fetchingAccount.Username, statusURL, false, false)
+ status, _, err := suite.dereferencer.GetRemoteStatus(context.Background(), fetchingAccount.Username, statusURL, false, false)
suite.NoError(err)
suite.NotNil(status)
- suite.NotNil(statusable)
- suite.True(new)
// status values should be set
suite.Equal("https://unknown-instance.com/users/brand_new_person/statuses/01FE4NTHKWW7THT67EF10EB839", status.URI)
@@ -80,11 +78,9 @@ func (suite *StatusTestSuite) TestDereferenceStatusWithMention() {
fetchingAccount := suite.testAccounts["local_account_1"]
statusURL := testrig.URLMustParse("https://unknown-instance.com/users/brand_new_person/statuses/01FE5Y30E3W4P7TRE0R98KAYQV")
- status, statusable, new, err := suite.dereferencer.GetRemoteStatus(context.Background(), fetchingAccount.Username, statusURL, false, false)
+ status, _, err := suite.dereferencer.GetRemoteStatus(context.Background(), fetchingAccount.Username, statusURL, false, false)
suite.NoError(err)
suite.NotNil(status)
- suite.NotNil(statusable)
- suite.True(new)
// status values should be set
suite.Equal("https://unknown-instance.com/users/brand_new_person/statuses/01FE5Y30E3W4P7TRE0R98KAYQV", status.URI)
@@ -135,11 +131,9 @@ func (suite *StatusTestSuite) TestDereferenceStatusWithImageAndNoContent() {
fetchingAccount := suite.testAccounts["local_account_1"]
statusURL := testrig.URLMustParse("https://turnip.farm/users/turniplover6969/statuses/70c53e54-3146-42d5-a630-83c8b6c7c042")
- status, statusable, new, err := suite.dereferencer.GetRemoteStatus(context.Background(), fetchingAccount.Username, statusURL, false, false)
+ status, _, err := suite.dereferencer.GetRemoteStatus(context.Background(), fetchingAccount.Username, statusURL, false, false)
suite.NoError(err)
suite.NotNil(status)
- suite.NotNil(statusable)
- suite.True(new)
// status values should be set
suite.Equal("https://turnip.farm/users/turniplover6969/statuses/70c53e54-3146-42d5-a630-83c8b6c7c042", status.URI)
diff --git a/internal/federation/dereferencing/thread.go b/internal/federation/dereferencing/thread.go
index f527b99cd..469defd5e 100644
--- a/internal/federation/dereferencing/thread.go
+++ b/internal/federation/dereferencing/thread.go
@@ -52,9 +52,9 @@ func (d *deref) DereferenceThread(ctx context.Context, username string, statusIR
}
// first make sure we have this status in our db
- _, statusable, _, err := d.GetRemoteStatus(ctx, username, statusIRI, true, false)
+ _, statusable, err := d.GetRemoteStatus(ctx, username, statusIRI, true, false)
if err != nil {
- return fmt.Errorf("DereferenceThread: error getting status with id %s: %s", statusIRI.String(), err)
+ return fmt.Errorf("DereferenceThread: error getting initial status with id %s: %s", statusIRI.String(), err)
}
// first iterate up through ancestors, dereferencing if necessary as we go
@@ -106,9 +106,8 @@ func (d *deref) iterateAncestors(ctx context.Context, username string, statusIRI
return d.iterateAncestors(ctx, username, *nextIRI)
}
- // If we reach here, we're looking at a remote status -- make sure we have it in our db by calling GetRemoteStatus
- // We call it with refresh to true because we want the statusable representation to parse inReplyTo from.
- _, statusable, _, err := d.GetRemoteStatus(ctx, username, &statusIRI, true, false)
+ // If we reach here, we're looking at a remote status
+ _, statusable, err := d.GetRemoteStatus(ctx, username, &statusIRI, true, false)
if err != nil {
l.Debugf("error getting remote status: %s", err)
return nil
@@ -220,8 +219,8 @@ func (d *deref) iterateDescendants(ctx context.Context, username string, statusI
foundReplies++
// get the remote statusable and put it in the db
- _, statusable, new, err := d.GetRemoteStatus(ctx, username, itemURI, false, false)
- if new && err == nil && statusable != nil {
+ _, statusable, err := d.GetRemoteStatus(ctx, username, itemURI, false, false)
+ if err == nil {
// now iterate descendants of *that* status
if err := d.iterateDescendants(ctx, username, *itemURI, statusable); err != nil {
continue
diff --git a/internal/federation/federatingdb/announce_test.go b/internal/federation/federatingdb/announce_test.go
new file mode 100644
index 000000000..f63910298
--- /dev/null
+++ b/internal/federation/federatingdb/announce_test.go
@@ -0,0 +1,93 @@
+/*
+ GoToSocial
+ Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org
+
+ 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 .
+*/
+
+package federatingdb_test
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/suite"
+ "github.com/superseriousbusiness/activity/streams/vocab"
+ "github.com/superseriousbusiness/gotosocial/internal/ap"
+ "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
+)
+
+type AnnounceTestSuite struct {
+ FederatingDBTestSuite
+}
+
+func (suite *AnnounceTestSuite) TestNewAnnounce() {
+ receivingAccount1 := suite.testAccounts["local_account_1"]
+ announcingAccount := suite.testAccounts["remote_account_1"]
+
+ ctx := createTestContext(receivingAccount1, announcingAccount)
+ announce1 := suite.testActivities["announce_forwarded_1_zork"]
+
+ err := suite.federatingDB.Announce(ctx, announce1.Activity.(vocab.ActivityStreamsAnnounce))
+ suite.NoError(err)
+
+ // should be a message heading to the processor now, which we can intercept here
+ msg := <-suite.fromFederator
+ suite.Equal(ap.ActivityAnnounce, msg.APObjectType)
+ suite.Equal(ap.ActivityCreate, msg.APActivityType)
+
+ boost, ok := msg.GTSModel.(*gtsmodel.Status)
+ suite.True(ok)
+ suite.Equal(announcingAccount.ID, boost.AccountID)
+
+ // only the URI will be set on the boosted status because it still needs to be dereferenced
+ suite.NotEmpty(boost.BoostOf.URI)
+}
+
+func (suite *AnnounceTestSuite) TestAnnounceTwice() {
+ receivingAccount1 := suite.testAccounts["local_account_1"]
+ receivingAccount2 := suite.testAccounts["local_account_2"]
+
+ announcingAccount := suite.testAccounts["remote_account_1"]
+
+ ctx1 := createTestContext(receivingAccount1, announcingAccount)
+ announce1 := suite.testActivities["announce_forwarded_1_zork"]
+
+ err := suite.federatingDB.Announce(ctx1, announce1.Activity.(vocab.ActivityStreamsAnnounce))
+ suite.NoError(err)
+
+ // should be a message heading to the processor now, which we can intercept here
+ msg := <-suite.fromFederator
+ suite.Equal(ap.ActivityAnnounce, msg.APObjectType)
+ suite.Equal(ap.ActivityCreate, msg.APActivityType)
+ boost, ok := msg.GTSModel.(*gtsmodel.Status)
+ suite.True(ok)
+ suite.Equal(announcingAccount.ID, boost.AccountID)
+
+ // only the URI will be set on the boosted status because it still needs to be dereferenced
+ suite.NotEmpty(boost.BoostOf.URI)
+
+ ctx2 := createTestContext(receivingAccount2, announcingAccount)
+ announce2 := suite.testActivities["announce_forwarded_1_turtle"]
+
+ err = suite.federatingDB.Announce(ctx2, announce2.Activity.(vocab.ActivityStreamsAnnounce))
+ suite.NoError(err)
+
+ // since this is a repeat announce with the same URI, just delivered to a different inbox,
+ // we should have nothing in the messages channel...
+ suite.Empty(suite.fromFederator)
+}
+
+func TestAnnounceTestSuite(t *testing.T) {
+ suite.Run(t, &AnnounceTestSuite{})
+}
diff --git a/internal/federation/federatingdb/federatingdb_test.go b/internal/federation/federatingdb/federatingdb_test.go
index 8e6c1802d..ded817c98 100644
--- a/internal/federation/federatingdb/federatingdb_test.go
+++ b/internal/federation/federatingdb/federatingdb_test.go
@@ -63,8 +63,9 @@ func (suite *FederatingDBTestSuite) SetupSuite() {
}
func (suite *FederatingDBTestSuite) SetupTest() {
- testrig.InitTestLog()
testrig.InitTestConfig()
+ testrig.InitTestLog()
+
suite.fedWorker = concurrency.NewWorkerPool[messages.FromFederator](-1, -1)
suite.fromFederator = make(chan messages.FromFederator, 10)
suite.fedWorker.SetProcessor(func(ctx context.Context, msg messages.FromFederator) error {
diff --git a/internal/federation/federator.go b/internal/federation/federator.go
index 61d7f3e17..6412c9ee1 100644
--- a/internal/federation/federator.go
+++ b/internal/federation/federator.go
@@ -62,7 +62,7 @@ type Federator interface {
GetRemoteAccount(ctx context.Context, username string, remoteAccountID *url.URL, blocking bool, refresh bool) (*gtsmodel.Account, error)
- GetRemoteStatus(ctx context.Context, username string, remoteStatusID *url.URL, refresh, includeParent bool) (*gtsmodel.Status, ap.Statusable, bool, error)
+ GetRemoteStatus(ctx context.Context, username string, remoteStatusID *url.URL, refetch, includeParent bool) (*gtsmodel.Status, ap.Statusable, error)
EnrichRemoteStatus(ctx context.Context, username string, status *gtsmodel.Status, includeParent bool) (*gtsmodel.Status, error)
GetRemoteInstance(ctx context.Context, username string, remoteInstanceURI *url.URL) (*gtsmodel.Instance, error)
diff --git a/internal/processing/fromfederator.go b/internal/processing/fromfederator.go
index 06df17d91..a2eaa9bd1 100644
--- a/internal/processing/fromfederator.go
+++ b/internal/processing/fromfederator.go
@@ -108,7 +108,7 @@ func (p *processor) processCreateStatusFromFederator(ctx context.Context, federa
return errors.New("ProcessFromFederator: status was not pinned to federatorMsg, and neither was an IRI for us to dereference")
}
var err error
- status, _, _, err = p.federator.GetRemoteStatus(ctx, federatorMsg.ReceivingAccount.Username, federatorMsg.APIri, false, false)
+ status, _, err = p.federator.GetRemoteStatus(ctx, federatorMsg.ReceivingAccount.Username, federatorMsg.APIri, false, false)
if err != nil {
return err
}
diff --git a/internal/processing/processor_test.go b/internal/processing/processor_test.go
index 5946e6718..7032f8e28 100644
--- a/internal/processing/processor_test.go
+++ b/internal/processing/processor_test.go
@@ -38,7 +38,6 @@
"github.com/superseriousbusiness/gotosocial/internal/messages"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
"github.com/superseriousbusiness/gotosocial/internal/processing"
- "github.com/superseriousbusiness/gotosocial/internal/timeline"
"github.com/superseriousbusiness/gotosocial/internal/transport"
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
"github.com/superseriousbusiness/gotosocial/testrig"
@@ -54,7 +53,6 @@ type ProcessingStandardTestSuite struct {
transportController transport.Controller
federator federation.Federator
oauthServer oauth.Server
- timelineManager timeline.Manager
emailSender email.Sender
// standard suite models
diff --git a/internal/processing/search.go b/internal/processing/search.go
index 6c3d7da39..03f158187 100644
--- a/internal/processing/search.go
+++ b/internal/processing/search.go
@@ -127,7 +127,7 @@ func (p *processor) searchStatusByURI(ctx context.Context, authed *oauth.Auth, u
// we don't have it locally so dereference it if we're allowed to
if resolve {
- status, _, _, err := p.federator.GetRemoteStatus(ctx, authed.Account.Username, uri, true, true)
+ status, _, err := p.federator.GetRemoteStatus(ctx, authed.Account.Username, uri, false, true)
if err == nil {
if err := p.federator.DereferenceRemoteThread(ctx, authed.Account.Username, uri); err != nil {
// try to deref the thread while we're here
diff --git a/internal/typeutils/converter_test.go b/internal/typeutils/converter_test.go
index d85cb1b16..4d782c822 100644
--- a/internal/typeutils/converter_test.go
+++ b/internal/typeutils/converter_test.go
@@ -427,8 +427,8 @@ type TypeUtilsTestSuite struct {
}
func (suite *TypeUtilsTestSuite) SetupSuite() {
- testrig.InitTestLog()
testrig.InitTestConfig()
+ testrig.InitTestLog()
suite.db = testrig.NewTestDB()
suite.testAccounts = testrig.NewTestAccounts()
diff --git a/internal/typeutils/internaltoas_test.go b/internal/typeutils/internaltoas_test.go
index 72b928237..53403669e 100644
--- a/internal/typeutils/internaltoas_test.go
+++ b/internal/typeutils/internaltoas_test.go
@@ -21,7 +21,7 @@
import (
"context"
"encoding/json"
- "fmt"
+ "strings"
"testing"
"github.com/stretchr/testify/assert"
@@ -45,8 +45,11 @@ func (suite *InternalToASTestSuite) TestAccountToAS() {
bytes, err := json.Marshal(ser)
suite.NoError(err)
- fmt.Println(string(bytes))
- // TODO: write assertions here, rn we're just eyeballing the output
+ // trim off everything up to 'discoverable';
+ // this is necessary because the order of multiple 'context' entries is not determinate
+ trimmed := strings.Split(string(bytes), "\"discoverable\"")[1]
+
+ suite.Equal(`:true,"featured":"http://localhost:8080/users/the_mighty_zork/collections/featured","followers":"http://localhost:8080/users/the_mighty_zork/followers","following":"http://localhost:8080/users/the_mighty_zork/following","icon":{"mediaType":"image/jpeg","type":"Image","url":"http://localhost:8080/fileserver/01F8MH1H7YV1Z7D2C8K2730QBF/avatar/original/01F8MH58A357CV5K7R7TJMSH6S.jpeg"},"id":"http://localhost:8080/users/the_mighty_zork","image":{"mediaType":"image/jpeg","type":"Image","url":"http://localhost:8080/fileserver/01F8MH1H7YV1Z7D2C8K2730QBF/header/original/01PFPMWK2FF0D9WMHEJHR07C3Q.jpeg"},"inbox":"http://localhost:8080/users/the_mighty_zork/inbox","manuallyApprovesFollowers":false,"name":"original zork (he/they)","outbox":"http://localhost:8080/users/the_mighty_zork/outbox","preferredUsername":"the_mighty_zork","publicKey":{"id":"http://localhost:8080/users/the_mighty_zork/main-key","owner":"http://localhost:8080/users/the_mighty_zork","publicKeyPem":"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwXTcOAvM1Jiw5Ffpk0qn\nr0cwbNvFe/5zQ+Tp7tumK/ZnT37o7X0FUEXrxNi+dkhmeJ0gsaiN+JQGNUewvpSk\nPIAXKvi908aSfCGjs7bGlJCJCuDuL5d6m7hZnP9rt9fJc70GElPpG0jc9fXwlz7T\nlsPb2ecatmG05Y4jPwdC+oN4MNCv9yQzEvCVMzl76EJaM602kIHC1CISn0rDFmYd\n9rSN7XPlNJw1F6PbpJ/BWQ+pXHKw3OEwNTETAUNYiVGnZU+B7a7bZC9f6/aPbJuV\nt8Qmg+UnDvW1Y8gmfHnxaWG2f5TDBvCHmcYtucIZPLQD4trAozC4ryqlmCWQNKbt\n0wIDAQAB\n-----END PUBLIC KEY-----\n"},"summary":"\u003cp\u003ehey yo this is my profile!\u003c/p\u003e","type":"Person","url":"http://localhost:8080/@the_mighty_zork"}`, trimmed)
}
func (suite *InternalToASTestSuite) TestOutboxToASCollection() {
diff --git a/internal/typeutils/internaltofrontend_test.go b/internal/typeutils/internaltofrontend_test.go
new file mode 100644
index 000000000..c7aa31905
--- /dev/null
+++ b/internal/typeutils/internaltofrontend_test.go
@@ -0,0 +1,58 @@
+/*
+ GoToSocial
+ Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org
+
+ 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 .
+*/
+
+package typeutils_test
+
+import (
+ "context"
+ "encoding/json"
+ "testing"
+
+ "github.com/stretchr/testify/suite"
+)
+
+type InternalToFrontendTestSuite struct {
+ TypeUtilsTestSuite
+}
+
+func (suite *InternalToFrontendTestSuite) TestAccountToFrontend() {
+ testAccount := suite.testAccounts["local_account_1"] // take zork for this test
+ apiAccount, err := suite.typeconverter.AccountToAPIAccountPublic(context.Background(), testAccount)
+ suite.NoError(err)
+ suite.NotNil(apiAccount)
+
+ b, err := json.Marshal(apiAccount)
+ suite.NoError(err)
+ suite.Equal(`{"id":"01F8MH1H7YV1Z7D2C8K2730QBF","username":"the_mighty_zork","acct":"the_mighty_zork","display_name":"original zork (he/they)","locked":false,"bot":false,"created_at":"2022-05-20T11:09:18Z","note":"\u003cp\u003ehey yo this is my profile!\u003c/p\u003e","url":"http://localhost:8080/@the_mighty_zork","avatar":"http://localhost:8080/fileserver/01F8MH1H7YV1Z7D2C8K2730QBF/avatar/original/01F8MH58A357CV5K7R7TJMSH6S.jpeg","avatar_static":"http://localhost:8080/fileserver/01F8MH1H7YV1Z7D2C8K2730QBF/avatar/small/01F8MH58A357CV5K7R7TJMSH6S.jpeg","header":"http://localhost:8080/fileserver/01F8MH1H7YV1Z7D2C8K2730QBF/header/original/01PFPMWK2FF0D9WMHEJHR07C3Q.jpeg","header_static":"http://localhost:8080/fileserver/01F8MH1H7YV1Z7D2C8K2730QBF/header/small/01PFPMWK2FF0D9WMHEJHR07C3Q.jpeg","followers_count":2,"following_count":2,"statuses_count":5,"last_status_at":"2022-05-20T11:37:55Z","emojis":[],"fields":[]}`, string(b))
+}
+
+func (suite *InternalToFrontendTestSuite) TestStatusToFrontend() {
+ testStatus := suite.testStatuses["admin_account_status_1"]
+ requestingAccount := suite.testAccounts["local_account_1"]
+ apiStatus, err := suite.typeconverter.StatusToAPIStatus(context.Background(), testStatus, requestingAccount)
+ suite.NoError(err)
+
+ b, err := json.Marshal(apiStatus)
+ suite.NoError(err)
+
+ suite.Equal(`{"id":"01F8MH75CBF9JFX4ZAD54N0W0R","created_at":"2021-10-20T11:36:45Z","sensitive":false,"spoiler_text":"","visibility":"public","language":"en","uri":"http://localhost:8080/users/admin/statuses/01F8MH75CBF9JFX4ZAD54N0W0R","url":"http://localhost:8080/@admin/statuses/01F8MH75CBF9JFX4ZAD54N0W0R","replies_count":0,"reblogs_count":0,"favourites_count":1,"favourited":true,"reblogged":false,"muted":false,"bookmarked":false,"content":"hello world! #welcome ! first post on the instance :rainbow: !","application":{"name":"superseriousbusiness","website":"https://superserious.business"},"account":{"id":"01F8MH17FWEB39HZJ76B6VXSKF","username":"admin","acct":"admin","display_name":"","locked":false,"bot":false,"created_at":"2022-05-17T13:10:59Z","note":"","url":"http://localhost:8080/@admin","avatar":"","avatar_static":"","header":"","header_static":"","followers_count":1,"following_count":1,"statuses_count":4,"last_status_at":"2021-10-20T10:41:37Z","emojis":[],"fields":[]},"media_attachments":[{"id":"01F8MH6NEM8D7527KZAECTCR76","type":"image","url":"http://localhost:8080/fileserver/01F8MH17FWEB39HZJ76B6VXSKF/attachment/original/01F8MH6NEM8D7527KZAECTCR76.jpeg","preview_url":"http://localhost:8080/fileserver/01F8MH17FWEB39HZJ76B6VXSKF/attachment/small/01F8MH6NEM8D7527KZAECTCR76.jpeg","meta":{"original":{"width":1200,"height":630,"size":"1200x630","aspect":1.9047619},"small":{"width":256,"height":134,"size":"256x134","aspect":1.9104477},"focus":{"x":0,"y":0}},"description":"Black and white image of some 50's style text saying: Welcome On Board","blurhash":"LNJRdVM{00Rj%Mayt7j[4nWBofRj"}],"mentions":[],"tags":[{"name":"welcome","url":"http://localhost:8080/tags/welcome"}],"emojis":[{"shortcode":"rainbow","url":"http://localhost:8080/fileserver/01F8MH261H1KSV3GW3016GZRY3/emoji/original/01F8MH9H8E4VG3KDYJR9EGPXCQ.png","static_url":"http://localhost:8080/fileserver/01F8MH261H1KSV3GW3016GZRY3/emoji/static/01F8MH9H8E4VG3KDYJR9EGPXCQ.png","visible_in_picker":true}],"card":null,"poll":null,"text":""}`, string(b))
+}
+
+func TestInternalToFrontendTestSuite(t *testing.T) {
+ suite.Run(t, new(InternalToFrontendTestSuite))
+}
diff --git a/testrig/testmodels.go b/testrig/testmodels.go
index 70ab293b9..98dcdfefb 100644
--- a/testrig/testmodels.go
+++ b/testrig/testmodels.go
@@ -31,6 +31,7 @@
"net/http"
"net/url"
"os"
+ "sort"
"strings"
"time"
@@ -318,8 +319,8 @@ func NewTestAccounts() map[string]*gtsmodel.Account {
NoteRaw: "",
Memorial: false,
MovedToAccountID: "",
- CreatedAt: time.Now().Add(-72 * time.Hour),
- UpdatedAt: time.Now().Add(-72 * time.Hour),
+ CreatedAt: TimeMustParse("2022-05-17T13:10:59Z"),
+ UpdatedAt: TimeMustParse("2022-05-17T13:10:59Z"),
Bot: false,
Reason: "",
Locked: false,
@@ -357,8 +358,8 @@ func NewTestAccounts() map[string]*gtsmodel.Account {
NoteRaw: "hey yo this is my profile!",
Memorial: false,
MovedToAccountID: "",
- CreatedAt: time.Now().Add(-48 * time.Hour),
- UpdatedAt: time.Now().Add(-48 * time.Hour),
+ CreatedAt: TimeMustParse("2022-05-20T11:09:18Z"),
+ UpdatedAt: TimeMustParse("2022-05-20T11:09:18Z"),
Bot: false,
Reason: "I wanna be on this damned webbed site so bad! Please! Wow",
Locked: false,
@@ -496,6 +497,15 @@ func NewTestAccounts() map[string]*gtsmodel.Account {
},
}
+ var accountsSorted []*gtsmodel.Account
+ for _, a := range accounts {
+ accountsSorted = append(accountsSorted, a)
+ }
+
+ sort.Slice(accountsSorted, func(i, j int) bool {
+ return accountsSorted[i].ID > accountsSorted[j].ID
+ })
+
preserializedKeys := []string{
"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",
@@ -505,7 +515,7 @@ func NewTestAccounts() map[string]*gtsmodel.Account {
"MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDSIsx0TsUCeSHXDYPzViqRwB/wZhBkj5f0Mrc+Q0yogUmiTcubYQcf/xj9LOvtArJ+8/rori0j8aFX17jZqtFyDDINyhICT+i5bk1ZKPt/uH/H5oFpjtsL+bCoOF8F4AUeELExH0dO3uwl8v9fPZZ3AZEGj6UB6Ru13LON7fKHt+JT6s9jNtUIUpHUDg2GZYv9gLFGDDm9H91Yervl8yF6VWbK+7pcVyhlz5wqHR/qNUiyUXhiie+veiJc9ipCU7RriNEuehvF12d3rRIOK/wRsFAG4LxufJS8Shu8VJrOBlKzsufqjDZtnZb8SrTY0EjLJpslMf67zRDD1kEDpq4jAgMBAAECggEBAMeKxe2YMxpjHpBRRECZTTk0YN/ue5iShrAcTMeyLqRAiUS3bSXyIErw+bDIrIxXKFrHoja71x+vvw9kSSNhQxxymkFf5nQNn6geJxMIiLJC6AxSRgeP4U/g3jEPvqQck592KFzGH/e0Vji/JGMzX6NIeIfrdbx3uJmcp2CaWNkoOs7UYV5VbNDaIWYcgptQS9hJpCQ+cuMov7scXE88uKtwAl+0VVopNr/XA7vV+npsESBCt3dfnp6poA13ldfqReLdPTmDWH7Z8QrTIagrfPi5mKpxksTYyC0/quKyk4yTj8Ge5GWmsXCHtyf19NX7reeJa8MjEWonYDCdnqReDoECgYEA8R5OHNIGC6yw6ZyTuyEt2epXwUj0h2Z9d+JAT9ndRGK9xdMqJt4acjxfcEck2wjv9BuNLr5YvLc4CYiOgyqJHNt5c5Ys5rJEOgBZ2IFoaoXZNom2LEtr583T4RFXp/Id8ix85D6EZj8Hp6OvZygQFwEYQexY383hZZh5enkorUECgYEA3xr3u/SbttM86ib1RP1uuON9ZURfzpmrr2ubSWiRDqwift0T2HesdhWi6xDGjzGyeT5e7irf1BsBKUq2dp/wFX6+15A6eV12C7PvC4N8u3NJwGBdvCmufh5wZ19rerelaB7+vG9c+Nbw9h1BbDi8MlGs06oVSawvwUzp2oVKLmMCgYEAq1RFXOU/tnv3GYhQ0N86nWWPBaC5YJzK+qyh1huQxk8DWdY6VXPshs+vYTCsV5d6KZKKN3S5yR7Hir6lxT4sP30UR7WmIib5o90r+lO5xjdlqQMhl0fgXM48h+iyyHuaG8LQ274whhazccM1l683/6Cfg/hVDnJUfsRhTU1aQgECgYBrZPTZcf6+u+I3qHcqNYBl2YPUCly/+7LsJzVB2ebxlCSqwsq5yamn0fRxiMq7xSVvPXm+1b6WwEUH1mIMqiKMhk1hQJkVMMsRCRVJioqxROa8hua4G6xWI1riN8lp8hraCwl+NXEgi37ESgLjEFBvPGegH+BNbWgzeU2clcrGlwKBgHBxlFLf6AjDxjR8Z5dnZVPyvLOUjejs5nsLdOfONJ8F/MU0PoKFWdBavhbnwXwium6NvcearnhbWL758sKooZviQL6m/sKDGWMq3O8SCnX+TKTEOw+kLLFn4L3sT02WaHYg+C5iVEDdGlsXSehhI2e7hBoTulE/zbUkbA3+wlmv",
}
- if diff := len(accounts) - len(preserializedKeys); diff > 0 {
+ if diff := len(accountsSorted) - len(preserializedKeys); diff > 0 {
keyStrings := make([]string, diff)
for i := 0; i < diff; i++ {
priv, _ := rsa.GenerateKey(rand.Reader, 2048)
@@ -515,9 +525,7 @@ func NewTestAccounts() map[string]*gtsmodel.Account {
panic(fmt.Sprintf("mismatch between number of hardcoded test RSA keys and accounts used for test data. Insert the following generated key[s]: \n%+v", keyStrings))
}
- // generate keys for each account
- i := 0
- for _, v := range accounts {
+ for i, v := range accountsSorted {
premadeBytes, err := base64.StdEncoding.DecodeString(preserializedKeys[i])
if err != nil {
panic(err)
@@ -532,8 +540,8 @@ func NewTestAccounts() map[string]*gtsmodel.Account {
}
v.PrivateKey = priv
v.PublicKey = &priv.PublicKey
- i++
}
+
return accounts
}
@@ -1174,8 +1182,8 @@ func NewTestStatuses() map[string]*gtsmodel.Status {
URL: "http://localhost:8080/@the_mighty_zork/statuses/01FCTA44PW9H1TB328S9AQXKDS",
Content: "hi!",
AttachmentIDs: []string{},
- CreatedAt: time.Now().Add(-1 * time.Minute),
- UpdatedAt: time.Now().Add(-1 * time.Minute),
+ CreatedAt: TimeMustParse("2022-05-20T11:37:55Z"),
+ UpdatedAt: TimeMustParse("2022-05-20T11:37:55Z"),
Local: true,
AccountURI: "http://localhost:8080/users/the_mighty_zork",
AccountID: "01F8MH1H7YV1Z7D2C8K2730QBF",
@@ -1636,7 +1644,13 @@ func NewTestActivities(accounts map[string]*gtsmodel.Account) map[string]Activit
nil,
false,
[]vocab.ActivityStreamsMention{},
- nil,
+ []vocab.ActivityStreamsImage{
+ newAPImage(
+ URLMustParse("http://example.org/users/some_user/statuses/afaba698-5740-4e32-a702-af61aa543bc1/attachment1.jpeg"),
+ "image/jpeg",
+ "trent reznor looking handsome as balls",
+ "LEDara58O=t5EMSOENEN9]}?aK%0"),
+ },
)
createForwardedMessage := WrapAPNoteInCreate(
URLMustParse("http://example.org/users/some_user/statuses/afaba698-5740-4e32-a702-af61aa543bc1/activity"),
@@ -1645,6 +1659,33 @@ func NewTestActivities(accounts map[string]*gtsmodel.Account) map[string]Activit
forwardedMessage)
createForwardedMessageSig, createForwardedMessageDigest, createForwardedMessageDate := GetSignatureForActivity(createForwardedMessage, accounts["remote_account_1"].PublicKeyURI, accounts["remote_account_1"].PrivateKey, URLMustParse(accounts["local_account_1"].InboxURI))
+ announceForwarded1Zork := newAPAnnounce(
+ URLMustParse("http://fossbros-anonymous.io/users/foss_satan/first_announce"),
+ URLMustParse("http://fossbros-anonymous.io/users/foss_satan"),
+ time.Now(),
+ URLMustParse("http://fossbros-anonymous.io/users/foss_satan/followers"),
+ forwardedMessage,
+ )
+ announceForwarded1ZorkSig, announceForwarded1ZorkDigest, announceForwarded1ZorkDate := GetSignatureForActivity(announceForwarded1Zork, accounts["remote_account_1"].PublicKeyURI, accounts["remote_account_1"].PrivateKey, URLMustParse(accounts["local_account_1"].InboxURI))
+
+ announceForwarded1Turtle := newAPAnnounce(
+ URLMustParse("http://fossbros-anonymous.io/users/foss_satan/first_announce"),
+ URLMustParse("http://fossbros-anonymous.io/users/foss_satan"),
+ time.Now(),
+ URLMustParse("http://fossbros-anonymous.io/users/foss_satan/followers"),
+ forwardedMessage,
+ )
+ announceForwarded1TurtleSig, announceForwarded1TurtleDigest, announceForwarded1TurtleDate := GetSignatureForActivity(announceForwarded1Turtle, accounts["remote_account_1"].PublicKeyURI, accounts["remote_account_1"].PrivateKey, URLMustParse(accounts["local_account_2"].InboxURI))
+
+ announceForwarded2Zork := newAPAnnounce(
+ URLMustParse("http://fossbros-anonymous.io/users/foss_satan/second_announce"),
+ URLMustParse("http://fossbros-anonymous.io/users/foss_satan"),
+ time.Now(),
+ URLMustParse("http://fossbros-anonymous.io/users/foss_satan/followers"),
+ forwardedMessage,
+ )
+ announceForwarded2ZorkSig, announceForwarded2ZorkDigest, announceForwarded2ZorkDate := GetSignatureForActivity(announceForwarded2Zork, accounts["remote_account_1"].PublicKeyURI, accounts["remote_account_1"].PrivateKey, URLMustParse(accounts["local_account_1"].InboxURI))
+
return map[string]ActivityWithSignature{
"dm_for_zork": {
Activity: createDmForZork,
@@ -1670,6 +1711,24 @@ func NewTestActivities(accounts map[string]*gtsmodel.Account) map[string]Activit
DigestHeader: createForwardedMessageDigest,
DateHeader: createForwardedMessageDate,
},
+ "announce_forwarded_1_zork": {
+ Activity: announceForwarded1Zork,
+ SignatureHeader: announceForwarded1ZorkSig,
+ DigestHeader: announceForwarded1ZorkDigest,
+ DateHeader: announceForwarded1ZorkDate,
+ },
+ "announce_forwarded_1_turtle": {
+ Activity: announceForwarded1Turtle,
+ SignatureHeader: announceForwarded1TurtleSig,
+ DigestHeader: announceForwarded1TurtleDigest,
+ DateHeader: announceForwarded1TurtleDate,
+ },
+ "announce_forwarded_2_zork": {
+ Activity: announceForwarded2Zork,
+ SignatureHeader: announceForwarded2ZorkSig,
+ DigestHeader: announceForwarded2ZorkDigest,
+ DateHeader: announceForwarded2ZorkDate,
+ },
}
}
@@ -2585,3 +2644,41 @@ func WrapAPNoteInCreate(createID *url.URL, createActor *url.URL, createPublished
return create
}
+
+func newAPAnnounce(announceID *url.URL, announceActor *url.URL, announcePublished time.Time, announceTo *url.URL, announceNote vocab.ActivityStreamsNote) vocab.ActivityStreamsAnnounce {
+ announce := streams.NewActivityStreamsAnnounce()
+
+ if announceID != nil {
+ id := streams.NewJSONLDIdProperty()
+ id.Set(announceID)
+ announce.SetJSONLDId(id)
+ }
+
+ if announceActor != nil {
+ actor := streams.NewActivityStreamsActorProperty()
+ actor.AppendIRI(announceActor)
+ announce.SetActivityStreamsActor(actor)
+ }
+
+ if !announcePublished.IsZero() {
+ published := streams.NewActivityStreamsPublishedProperty()
+ published.Set(announcePublished)
+ announce.SetActivityStreamsPublished(published)
+ }
+
+ to := streams.NewActivityStreamsToProperty()
+ to.AppendIRI(announceTo)
+ announce.SetActivityStreamsTo(announceNote.GetActivityStreamsTo())
+
+ cc := streams.NewActivityStreamsCcProperty()
+ cc.AppendIRI(announceNote.GetActivityStreamsAttributedTo().Begin().GetIRI())
+ announce.SetActivityStreamsCc(cc)
+
+ if announceNote != nil {
+ noteIRI := streams.NewActivityStreamsObjectProperty()
+ noteIRI.AppendIRI(announceNote.GetJSONLDId().Get())
+ announce.SetActivityStreamsObject(noteIRI)
+ }
+
+ return announce
+}