mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2025-11-28 03:23:32 -06:00
[bugfix] Add Actor to outgoing poll vote Create; other fixes (#2384)
This commit is contained in:
parent
5c17ecd93a
commit
e4e0a5e3f6
6 changed files with 108 additions and 51 deletions
|
|
@ -194,14 +194,13 @@ func (f *federate) CreatePollVote(ctx context.Context, poll *gtsmodel.Poll, vote
|
|||
return err
|
||||
}
|
||||
|
||||
// Convert votes to AS PollOptionable implementing type.
|
||||
notes, err := f.converter.PollVoteToASOptions(ctx, vote)
|
||||
// Convert vote to AS Create with vote choices as Objects.
|
||||
create, err := f.converter.PollVoteToASCreate(ctx, vote)
|
||||
if err != nil {
|
||||
return gtserror.Newf("error converting to notes: %w", err)
|
||||
}
|
||||
|
||||
// Send a Create activity with PollOptionables via the Actor's outbox.
|
||||
create := typeutils.WrapPollOptionablesInCreate(notes...)
|
||||
// Send the Create via the Actor's outbox.
|
||||
if _, err := f.FederatingActor().Send(ctx, outboxIRI, create); err != nil {
|
||||
return gtserror.Newf("error sending Create activity via outbox %s: %w", outboxIRI, err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -480,6 +480,7 @@ type TypeUtilsTestSuite struct {
|
|||
testEmojis map[string]*gtsmodel.Emoji
|
||||
testReports map[string]*gtsmodel.Report
|
||||
testMentions map[string]*gtsmodel.Mention
|
||||
testPollVotes map[string]*gtsmodel.PollVote
|
||||
|
||||
typeconverter *typeutils.Converter
|
||||
}
|
||||
|
|
@ -502,6 +503,7 @@ func (suite *TypeUtilsTestSuite) SetupTest() {
|
|||
suite.testEmojis = testrig.NewTestEmojis()
|
||||
suite.testReports = testrig.NewTestReports()
|
||||
suite.testMentions = testrig.NewTestMentions()
|
||||
suite.testPollVotes = testrig.NewTestPollVotes()
|
||||
suite.typeconverter = typeutils.NewConverter(&suite.state)
|
||||
|
||||
testrig.StandardDBSetup(suite.db, nil)
|
||||
|
|
|
|||
|
|
@ -1659,7 +1659,17 @@ func (c *Converter) ReportToASFlag(ctx context.Context, r *gtsmodel.Report) (voc
|
|||
return flag, nil
|
||||
}
|
||||
|
||||
func (c *Converter) PollVoteToASOptions(ctx context.Context, vote *gtsmodel.PollVote) ([]ap.PollOptionable, error) {
|
||||
// PollVoteToASCreate converts a vote on a poll into a Create
|
||||
// activity, suitable for federation, with each choice in the
|
||||
// vote appended as a Note to the Create's Object field.
|
||||
func (c *Converter) PollVoteToASCreate(
|
||||
ctx context.Context,
|
||||
vote *gtsmodel.PollVote,
|
||||
) (vocab.ActivityStreamsCreate, error) {
|
||||
if len(vote.Choices) == 0 {
|
||||
panic("no vote.Choices")
|
||||
}
|
||||
|
||||
// Ensure the vote is fully populated (this fetches author).
|
||||
if err := c.state.DB.PopulatePollVote(ctx, vote); err != nil {
|
||||
return nil, gtserror.Newf("error populating vote from db: %w", err)
|
||||
|
|
@ -1694,11 +1704,22 @@ func (c *Converter) PollVoteToASOptions(ctx context.Context, vote *gtsmodel.Poll
|
|||
return nil, gtserror.Newf("invalid account uri: %w", err)
|
||||
}
|
||||
|
||||
// Preallocate the return slice of notes.
|
||||
notes := make([]ap.PollOptionable, len(vote.Choices))
|
||||
// Allocate Create activity and address 'To' poll author.
|
||||
create := streams.NewActivityStreamsCreate()
|
||||
ap.AppendTo(create, pollAuthorIRI)
|
||||
|
||||
for i, choice := range vote.Choices {
|
||||
// Create new note to represent vote.
|
||||
// Create ID formatted as: {$voterIRI}/activity#vote/{$statusIRI}.
|
||||
id := author.URI + "/activity#vote/" + poll.Status.URI
|
||||
ap.MustSet(ap.SetJSONLDIdStr, ap.WithJSONLDId(create), id)
|
||||
|
||||
// Set Create actor appropriately.
|
||||
ap.AppendActor(create, authorIRI)
|
||||
|
||||
// Set publish time for activity.
|
||||
ap.SetPublished(create, vote.CreatedAt)
|
||||
|
||||
// Parse each choice to a Note and add it to the Create.
|
||||
for _, choice := range vote.Choices {
|
||||
note := streams.NewActivityStreamsNote()
|
||||
|
||||
// For AP IRI generate from author URI + poll ID + vote choice.
|
||||
|
|
@ -1715,9 +1736,9 @@ func (c *Converter) PollVoteToASOptions(ctx context.Context, vote *gtsmodel.Poll
|
|||
ap.AppendInReplyTo(note, statusIRI)
|
||||
ap.AppendTo(note, pollAuthorIRI)
|
||||
|
||||
// Set note in return slice.
|
||||
notes[i] = note
|
||||
// Append this note as Create Object.
|
||||
appendStatusableToActivity(create, note, false)
|
||||
}
|
||||
|
||||
return notes, nil
|
||||
return create, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -879,6 +879,48 @@ func (suite *InternalToASTestSuite) TestPinnedStatusesToASOneItem() {
|
|||
}`, string(bytes))
|
||||
}
|
||||
|
||||
func (suite *InternalToASTestSuite) TestPollVoteToASCreate() {
|
||||
vote := suite.testPollVotes["remote_account_1_status_2_poll_vote_local_account_1"]
|
||||
|
||||
create, err := suite.typeconverter.PollVoteToASCreate(context.Background(), vote)
|
||||
if err != nil {
|
||||
suite.FailNow(err.Error())
|
||||
}
|
||||
|
||||
createI, err := ap.Serialize(create)
|
||||
suite.NoError(err)
|
||||
|
||||
bytes, err := json.MarshalIndent(createI, "", " ")
|
||||
suite.NoError(err)
|
||||
|
||||
suite.Equal(`{
|
||||
"@context": "https://www.w3.org/ns/activitystreams",
|
||||
"actor": "http://localhost:8080/users/the_mighty_zork",
|
||||
"id": "http://localhost:8080/users/the_mighty_zork/activity#vote/http://fossbros-anonymous.io/users/foss_satan/statuses/01HEN2QRFA8H3C6QPN7RD4KSR6",
|
||||
"object": [
|
||||
{
|
||||
"attributedTo": "http://localhost:8080/users/the_mighty_zork",
|
||||
"id": "http://localhost:8080/users/the_mighty_zork#01HEN2R65468ZG657C4ZPHJ4EX/votes/1",
|
||||
"inReplyTo": "http://fossbros-anonymous.io/users/foss_satan/statuses/01HEN2QRFA8H3C6QPN7RD4KSR6",
|
||||
"name": "tissues",
|
||||
"to": "http://fossbros-anonymous.io/users/foss_satan",
|
||||
"type": "Note"
|
||||
},
|
||||
{
|
||||
"attributedTo": "http://localhost:8080/users/the_mighty_zork",
|
||||
"id": "http://localhost:8080/users/the_mighty_zork#01HEN2R65468ZG657C4ZPHJ4EX/votes/2",
|
||||
"inReplyTo": "http://fossbros-anonymous.io/users/foss_satan/statuses/01HEN2QRFA8H3C6QPN7RD4KSR6",
|
||||
"name": "financial times",
|
||||
"to": "http://fossbros-anonymous.io/users/foss_satan",
|
||||
"type": "Note"
|
||||
}
|
||||
],
|
||||
"published": "2021-09-11T11:45:37+02:00",
|
||||
"to": "http://fossbros-anonymous.io/users/foss_satan",
|
||||
"type": "Create"
|
||||
}`, string(bytes))
|
||||
}
|
||||
|
||||
func TestInternalToASTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(InternalToASTestSuite))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ package typeutils
|
|||
|
||||
import (
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/superseriousbusiness/activity/pub"
|
||||
"github.com/superseriousbusiness/activity/streams"
|
||||
|
|
@ -91,43 +90,6 @@ func WrapStatusableInCreate(status ap.Statusable, iriOnly bool) vocab.ActivitySt
|
|||
return create
|
||||
}
|
||||
|
||||
func WrapPollOptionablesInCreate(options ...ap.PollOptionable) vocab.ActivityStreamsCreate {
|
||||
if len(options) == 0 {
|
||||
panic("no options")
|
||||
}
|
||||
|
||||
// Extract attributedTo IRI from any option.
|
||||
attribTos := ap.GetAttributedTo(options[0])
|
||||
if len(attribTos) != 1 {
|
||||
panic("invalid attributedTo count")
|
||||
}
|
||||
|
||||
// Extract target status IRI from any option.
|
||||
replyTos := ap.GetInReplyTo(options[0])
|
||||
if len(replyTos) != 1 {
|
||||
panic("invalid inReplyTo count")
|
||||
}
|
||||
|
||||
// Allocate create activity and copy over 'To' property.
|
||||
create := streams.NewActivityStreamsCreate()
|
||||
ap.AppendTo(create, ap.GetTo(options[0])...)
|
||||
|
||||
// Activity ID formatted as: {$statusIRI}/activity#vote/{$voterIRI}.
|
||||
id := replyTos[0].String() + "/activity#vote/" + attribTos[0].String()
|
||||
ap.MustSet(ap.SetJSONLDIdStr, ap.WithJSONLDId(create), id)
|
||||
|
||||
// Set a current publish time for activity.
|
||||
ap.SetPublished(create, time.Now())
|
||||
|
||||
// Append each poll option as object to activity.
|
||||
for _, option := range options {
|
||||
status, _ := ap.ToStatusable(option)
|
||||
appendStatusableToActivity(create, status, false)
|
||||
}
|
||||
|
||||
return create
|
||||
}
|
||||
|
||||
func WrapStatusableInUpdate(status ap.Statusable, iriOnly bool) vocab.ActivityStreamsUpdate {
|
||||
update := streams.NewActivityStreamsUpdate()
|
||||
wrapStatusableInActivity(update, status, iriOnly)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue