mirror of
https://github.com/superseriousbusiness/gotosocial
synced 2025-06-05 21:59:39 +02:00
[feature] Federate interaction policies + Accepts; enforce policies (#3138)
* [feature] Federate interaction policies + Accepts; enforce policies * use Acceptable type * fix index * remove appendIRIStrs * add GetAccept federatingdb function * lock on object IRI
This commit is contained in:
@@ -393,13 +393,23 @@ func (c *Converter) ASStatusToStatus(ctx context.Context, statusable ap.Statusab
|
||||
return nil, gtserror.SetMalformed(err)
|
||||
}
|
||||
|
||||
// Advanced visibility toggles for this status.
|
||||
//
|
||||
// TODO: a lot of work to be done here -- a new type
|
||||
// needs to be created for this in go-fed/activity.
|
||||
// Until this is implemented, assume all true.
|
||||
// Status was sent to us or dereffed
|
||||
// by us so it must be federated.
|
||||
status.Federated = util.Ptr(true)
|
||||
|
||||
// Derive interaction policy for this status.
|
||||
status.InteractionPolicy = ap.ExtractInteractionPolicy(
|
||||
statusable,
|
||||
status.Account,
|
||||
)
|
||||
|
||||
// Set approvedByURI if present,
|
||||
// for later dereferencing.
|
||||
approvedByURI := ap.GetApprovedBy(statusable)
|
||||
if approvedByURI != nil {
|
||||
status.ApprovedByURI = approvedByURI.String()
|
||||
}
|
||||
|
||||
// status.Sensitive
|
||||
sensitive := ap.ExtractSensitive(statusable)
|
||||
status.Sensitive = &sensitive
|
||||
|
@@ -36,6 +36,7 @@ import (
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/log"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/uris"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||
)
|
||||
|
||||
// AccountToAS converts a gts model account into an activity streams person, suitable for federation
|
||||
@@ -672,6 +673,38 @@ func (c *Converter) StatusToAS(ctx context.Context, s *gtsmodel.Status) (ap.Stat
|
||||
sensitiveProp.AppendXMLSchemaBoolean(*s.Sensitive)
|
||||
status.SetActivityStreamsSensitive(sensitiveProp)
|
||||
|
||||
// interactionPolicy
|
||||
var p *gtsmodel.InteractionPolicy
|
||||
if s.InteractionPolicy != nil {
|
||||
// Use InteractionPolicy
|
||||
// set on the status.
|
||||
p = s.InteractionPolicy
|
||||
} else {
|
||||
// Fall back to default policy
|
||||
// for the status's visibility.
|
||||
p = gtsmodel.DefaultInteractionPolicyFor(s.Visibility)
|
||||
}
|
||||
policy, err := c.InteractionPolicyToASInteractionPolicy(ctx, p, s)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating interactionPolicy: %w", err)
|
||||
}
|
||||
|
||||
policyProp := streams.NewGoToSocialInteractionPolicyProperty()
|
||||
policyProp.AppendGoToSocialInteractionPolicy(policy)
|
||||
status.SetGoToSocialInteractionPolicy(policyProp)
|
||||
|
||||
// Parse + set approvedBy.
|
||||
if s.ApprovedByURI != "" {
|
||||
approvedBy, err := url.Parse(s.ApprovedByURI)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing approvedBy: %w", err)
|
||||
}
|
||||
|
||||
approvedByProp := streams.NewGoToSocialApprovedByProperty()
|
||||
approvedByProp.Set(approvedBy)
|
||||
status.SetGoToSocialApprovedBy(approvedByProp)
|
||||
}
|
||||
|
||||
return status, nil
|
||||
}
|
||||
|
||||
@@ -1169,6 +1202,18 @@ func (c *Converter) FaveToAS(ctx context.Context, f *gtsmodel.StatusFave) (vocab
|
||||
toProp.AppendIRI(toIRI)
|
||||
like.SetActivityStreamsTo(toProp)
|
||||
|
||||
// Parse + set approvedBy.
|
||||
if f.ApprovedByURI != "" {
|
||||
approvedBy, err := url.Parse(f.ApprovedByURI)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing approvedBy: %w", err)
|
||||
}
|
||||
|
||||
approvedByProp := streams.NewGoToSocialApprovedByProperty()
|
||||
approvedByProp.Set(approvedBy)
|
||||
like.SetGoToSocialApprovedBy(approvedByProp)
|
||||
}
|
||||
|
||||
return like, nil
|
||||
}
|
||||
|
||||
@@ -1247,6 +1292,18 @@ func (c *Converter) BoostToAS(ctx context.Context, boostWrapperStatus *gtsmodel.
|
||||
|
||||
announce.SetActivityStreamsCc(ccProp)
|
||||
|
||||
// Parse + set approvedBy.
|
||||
if boostWrapperStatus.ApprovedByURI != "" {
|
||||
approvedBy, err := url.Parse(boostWrapperStatus.ApprovedByURI)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing approvedBy: %w", err)
|
||||
}
|
||||
|
||||
approvedByProp := streams.NewGoToSocialApprovedByProperty()
|
||||
approvedByProp.Set(approvedBy)
|
||||
announce.SetGoToSocialApprovedBy(approvedByProp)
|
||||
}
|
||||
|
||||
return announce, nil
|
||||
}
|
||||
|
||||
@@ -1724,3 +1781,227 @@ func (c *Converter) PollVoteToASCreate(
|
||||
|
||||
return create, nil
|
||||
}
|
||||
|
||||
// populateValuesForProp appends the given PolicyValues
|
||||
// to the given property, for the given status.
|
||||
func populateValuesForProp[T ap.WithIRI](
|
||||
prop ap.Property[T],
|
||||
status *gtsmodel.Status,
|
||||
urns gtsmodel.PolicyValues,
|
||||
) error {
|
||||
iriStrs := make([]string, 0)
|
||||
|
||||
for _, urn := range urns {
|
||||
switch urn {
|
||||
|
||||
case gtsmodel.PolicyValueAuthor:
|
||||
iriStrs = append(iriStrs, status.Account.URI)
|
||||
|
||||
case gtsmodel.PolicyValueMentioned:
|
||||
for _, m := range status.Mentions {
|
||||
iriStrs = append(iriStrs, m.TargetAccount.URI)
|
||||
}
|
||||
|
||||
case gtsmodel.PolicyValueFollowing:
|
||||
iriStrs = append(iriStrs, status.Account.FollowingURI)
|
||||
|
||||
case gtsmodel.PolicyValueFollowers:
|
||||
iriStrs = append(iriStrs, status.Account.FollowersURI)
|
||||
|
||||
case gtsmodel.PolicyValuePublic:
|
||||
iriStrs = append(iriStrs, pub.PublicActivityPubIRI)
|
||||
|
||||
default:
|
||||
iriStrs = append(iriStrs, string(urn))
|
||||
}
|
||||
}
|
||||
|
||||
// Deduplicate the iri strings to
|
||||
// make sure we're not parsing + adding
|
||||
// the same string multiple times.
|
||||
iriStrs = util.Deduplicate(iriStrs)
|
||||
|
||||
// Append them to the property.
|
||||
for _, iriStr := range iriStrs {
|
||||
iri, err := url.Parse(iriStr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
prop.AppendIRI(iri)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// InteractionPolicyToASInteractionPolicy returns a
|
||||
// GoToSocial interaction policy suitable for federation.
|
||||
func (c *Converter) InteractionPolicyToASInteractionPolicy(
|
||||
ctx context.Context,
|
||||
interactionPolicy *gtsmodel.InteractionPolicy,
|
||||
status *gtsmodel.Status,
|
||||
) (vocab.GoToSocialInteractionPolicy, error) {
|
||||
policy := streams.NewGoToSocialInteractionPolicy()
|
||||
|
||||
/*
|
||||
CAN LIKE
|
||||
*/
|
||||
|
||||
// Build canLike
|
||||
canLike := streams.NewGoToSocialCanLike()
|
||||
|
||||
// Build canLike.always
|
||||
canLikeAlwaysProp := streams.NewGoToSocialAlwaysProperty()
|
||||
if err := populateValuesForProp(
|
||||
canLikeAlwaysProp,
|
||||
status,
|
||||
interactionPolicy.CanLike.Always,
|
||||
); err != nil {
|
||||
return nil, gtserror.Newf("error setting canLike.always: %w", err)
|
||||
}
|
||||
|
||||
// Set canLike.always
|
||||
canLike.SetGoToSocialAlways(canLikeAlwaysProp)
|
||||
|
||||
// Build canLike.approvalRequired
|
||||
canLikeApprovalRequiredProp := streams.NewGoToSocialApprovalRequiredProperty()
|
||||
if err := populateValuesForProp(
|
||||
canLikeApprovalRequiredProp,
|
||||
status,
|
||||
interactionPolicy.CanLike.WithApproval,
|
||||
); err != nil {
|
||||
return nil, gtserror.Newf("error setting canLike.approvalRequired: %w", err)
|
||||
}
|
||||
|
||||
// Set canLike.approvalRequired.
|
||||
canLike.SetGoToSocialApprovalRequired(canLikeApprovalRequiredProp)
|
||||
|
||||
// Set canLike on the policy.
|
||||
canLikeProp := streams.NewGoToSocialCanLikeProperty()
|
||||
canLikeProp.AppendGoToSocialCanLike(canLike)
|
||||
policy.SetGoToSocialCanLike(canLikeProp)
|
||||
|
||||
/*
|
||||
CAN REPLY
|
||||
*/
|
||||
|
||||
// Build canReply
|
||||
canReply := streams.NewGoToSocialCanReply()
|
||||
|
||||
// Build canReply.always
|
||||
canReplyAlwaysProp := streams.NewGoToSocialAlwaysProperty()
|
||||
if err := populateValuesForProp(
|
||||
canReplyAlwaysProp,
|
||||
status,
|
||||
interactionPolicy.CanReply.Always,
|
||||
); err != nil {
|
||||
return nil, gtserror.Newf("error setting canReply.always: %w", err)
|
||||
}
|
||||
|
||||
// Set canReply.always
|
||||
canReply.SetGoToSocialAlways(canReplyAlwaysProp)
|
||||
|
||||
// Build canReply.approvalRequired
|
||||
canReplyApprovalRequiredProp := streams.NewGoToSocialApprovalRequiredProperty()
|
||||
if err := populateValuesForProp(
|
||||
canReplyApprovalRequiredProp,
|
||||
status,
|
||||
interactionPolicy.CanReply.WithApproval,
|
||||
); err != nil {
|
||||
return nil, gtserror.Newf("error setting canReply.approvalRequired: %w", err)
|
||||
}
|
||||
|
||||
// Set canReply.approvalRequired.
|
||||
canReply.SetGoToSocialApprovalRequired(canReplyApprovalRequiredProp)
|
||||
|
||||
// Set canReply on the policy.
|
||||
canReplyProp := streams.NewGoToSocialCanReplyProperty()
|
||||
canReplyProp.AppendGoToSocialCanReply(canReply)
|
||||
policy.SetGoToSocialCanReply(canReplyProp)
|
||||
|
||||
/*
|
||||
CAN ANNOUNCE
|
||||
*/
|
||||
|
||||
// Build canAnnounce
|
||||
canAnnounce := streams.NewGoToSocialCanAnnounce()
|
||||
|
||||
// Build canAnnounce.always
|
||||
canAnnounceAlwaysProp := streams.NewGoToSocialAlwaysProperty()
|
||||
if err := populateValuesForProp(
|
||||
canAnnounceAlwaysProp,
|
||||
status,
|
||||
interactionPolicy.CanAnnounce.Always,
|
||||
); err != nil {
|
||||
return nil, gtserror.Newf("error setting canAnnounce.always: %w", err)
|
||||
}
|
||||
|
||||
// Set canAnnounce.always
|
||||
canAnnounce.SetGoToSocialAlways(canAnnounceAlwaysProp)
|
||||
|
||||
// Build canAnnounce.approvalRequired
|
||||
canAnnounceApprovalRequiredProp := streams.NewGoToSocialApprovalRequiredProperty()
|
||||
if err := populateValuesForProp(
|
||||
canAnnounceApprovalRequiredProp,
|
||||
status,
|
||||
interactionPolicy.CanAnnounce.WithApproval,
|
||||
); err != nil {
|
||||
return nil, gtserror.Newf("error setting canAnnounce.approvalRequired: %w", err)
|
||||
}
|
||||
|
||||
// Set canAnnounce.approvalRequired.
|
||||
canAnnounce.SetGoToSocialApprovalRequired(canAnnounceApprovalRequiredProp)
|
||||
|
||||
// Set canAnnounce on the policy.
|
||||
canAnnounceProp := streams.NewGoToSocialCanAnnounceProperty()
|
||||
canAnnounceProp.AppendGoToSocialCanAnnounce(canAnnounce)
|
||||
policy.SetGoToSocialCanAnnounce(canAnnounceProp)
|
||||
|
||||
return policy, nil
|
||||
}
|
||||
|
||||
// InteractionApprovalToASAccept converts a *gtsmodel.InteractionApproval
|
||||
// to an ActivityStreams Accept, addressed to the interacting account.
|
||||
func (c *Converter) InteractionApprovalToASAccept(
|
||||
ctx context.Context,
|
||||
approval *gtsmodel.InteractionApproval,
|
||||
) (vocab.ActivityStreamsAccept, error) {
|
||||
accept := streams.NewActivityStreamsAccept()
|
||||
|
||||
acceptID, err := url.Parse(approval.URI)
|
||||
if err != nil {
|
||||
return nil, gtserror.Newf("invalid accept uri: %w", err)
|
||||
}
|
||||
|
||||
actorIRI, err := url.Parse(approval.Account.URI)
|
||||
if err != nil {
|
||||
return nil, gtserror.Newf("invalid account uri: %w", err)
|
||||
}
|
||||
|
||||
objectIRI, err := url.Parse(approval.InteractionURI)
|
||||
if err != nil {
|
||||
return nil, gtserror.Newf("invalid target uri: %w", err)
|
||||
}
|
||||
|
||||
toIRI, err := url.Parse(approval.InteractingAccount.URI)
|
||||
if err != nil {
|
||||
return nil, gtserror.Newf("invalid interacting account uri: %w", err)
|
||||
}
|
||||
|
||||
// Set id to the URI of
|
||||
// interactionApproval.
|
||||
ap.SetJSONLDId(accept, acceptID)
|
||||
|
||||
// Actor is the account that
|
||||
// owns the approval / accept.
|
||||
ap.AppendActorIRIs(accept, actorIRI)
|
||||
|
||||
// Object is the interaction URI.
|
||||
ap.AppendObjectIRIs(accept, objectIRI)
|
||||
|
||||
// Address to the owner
|
||||
// of interaction URI.
|
||||
ap.AppendTo(accept, toIRI)
|
||||
|
||||
return accept, nil
|
||||
}
|
||||
|
@@ -21,8 +21,6 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/suite"
|
||||
@@ -46,14 +44,15 @@ func (suite *InternalToASTestSuite) TestAccountToAS() {
|
||||
ser, err := ap.Serialize(asPerson)
|
||||
suite.NoError(err)
|
||||
|
||||
// Drop "@context" property as
|
||||
// the ordering is non-determinate.
|
||||
delete(ser, "@context")
|
||||
|
||||
bytes, err := json.MarshalIndent(ser, "", " ")
|
||||
suite.NoError(err)
|
||||
|
||||
// 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,
|
||||
suite.Equal(`{
|
||||
"discoverable": 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",
|
||||
@@ -82,7 +81,7 @@ func (suite *InternalToASTestSuite) TestAccountToAS() {
|
||||
"tag": [],
|
||||
"type": "Person",
|
||||
"url": "http://localhost:8080/@the_mighty_zork"
|
||||
}`, trimmed)
|
||||
}`, string(bytes))
|
||||
}
|
||||
|
||||
func (suite *InternalToASTestSuite) TestAccountToASWithFields() {
|
||||
@@ -95,16 +94,15 @@ func (suite *InternalToASTestSuite) TestAccountToASWithFields() {
|
||||
ser, err := ap.Serialize(asPerson)
|
||||
suite.NoError(err)
|
||||
|
||||
// Drop "@context" property as
|
||||
// the ordering is non-determinate.
|
||||
delete(ser, "@context")
|
||||
|
||||
bytes, err := json.MarshalIndent(ser, "", " ")
|
||||
suite.NoError(err)
|
||||
|
||||
// trim off everything up to 'attachment';
|
||||
// this is necessary because the order of multiple 'context' entries is not determinate
|
||||
trimmed := strings.Split(string(bytes), "\"attachment\"")[1]
|
||||
|
||||
fmt.Printf("\n\n\n%s\n\n\n", string(bytes))
|
||||
|
||||
suite.Equal(`: [
|
||||
suite.Equal(`{
|
||||
"attachment": [
|
||||
{
|
||||
"name": "should you follow me?",
|
||||
"type": "PropertyValue",
|
||||
@@ -135,7 +133,7 @@ func (suite *InternalToASTestSuite) TestAccountToASWithFields() {
|
||||
"tag": [],
|
||||
"type": "Person",
|
||||
"url": "http://localhost:8080/@1happyturtle"
|
||||
}`, trimmed)
|
||||
}`, string(bytes))
|
||||
}
|
||||
|
||||
func (suite *InternalToASTestSuite) TestAccountToASAliasedAndMoved() {
|
||||
@@ -161,14 +159,15 @@ func (suite *InternalToASTestSuite) TestAccountToASAliasedAndMoved() {
|
||||
ser, err := ap.Serialize(asPerson)
|
||||
suite.NoError(err)
|
||||
|
||||
// Drop "@context" property as
|
||||
// the ordering is non-determinate.
|
||||
delete(ser, "@context")
|
||||
|
||||
bytes, err := json.MarshalIndent(ser, "", " ")
|
||||
suite.NoError(err)
|
||||
|
||||
// trim off everything up to 'alsoKnownAs';
|
||||
// this is necessary because the order of multiple 'context' entries is not determinate
|
||||
trimmed := strings.Split(string(bytes), "\"alsoKnownAs\"")[1]
|
||||
|
||||
suite.Equal(`: [
|
||||
suite.Equal(`{
|
||||
"alsoKnownAs": [
|
||||
"http://localhost:8080/users/1happyturtle"
|
||||
],
|
||||
"discoverable": true,
|
||||
@@ -201,7 +200,7 @@ func (suite *InternalToASTestSuite) TestAccountToASAliasedAndMoved() {
|
||||
"tag": [],
|
||||
"type": "Person",
|
||||
"url": "http://localhost:8080/@the_mighty_zork"
|
||||
}`, trimmed)
|
||||
}`, string(bytes))
|
||||
}
|
||||
|
||||
func (suite *InternalToASTestSuite) TestAccountToASWithOneField() {
|
||||
@@ -215,15 +214,16 @@ func (suite *InternalToASTestSuite) TestAccountToASWithOneField() {
|
||||
ser, err := ap.Serialize(asPerson)
|
||||
suite.NoError(err)
|
||||
|
||||
// Drop "@context" property as
|
||||
// the ordering is non-determinate.
|
||||
delete(ser, "@context")
|
||||
|
||||
bytes, err := json.MarshalIndent(ser, "", " ")
|
||||
suite.NoError(err)
|
||||
|
||||
// trim off everything up to 'attachment';
|
||||
// this is necessary because the order of multiple 'context' entries is not determinate
|
||||
trimmed := strings.Split(string(bytes), "\"attachment\"")[1]
|
||||
|
||||
// Despite only one field being set, attachments should still be a slice/array.
|
||||
suite.Equal(`: [
|
||||
suite.Equal(`{
|
||||
"attachment": [
|
||||
{
|
||||
"name": "should you follow me?",
|
||||
"type": "PropertyValue",
|
||||
@@ -249,7 +249,7 @@ func (suite *InternalToASTestSuite) TestAccountToASWithOneField() {
|
||||
"tag": [],
|
||||
"type": "Person",
|
||||
"url": "http://localhost:8080/@1happyturtle"
|
||||
}`, trimmed)
|
||||
}`, string(bytes))
|
||||
}
|
||||
|
||||
func (suite *InternalToASTestSuite) TestAccountToASWithEmoji() {
|
||||
@@ -263,14 +263,15 @@ func (suite *InternalToASTestSuite) TestAccountToASWithEmoji() {
|
||||
ser, err := ap.Serialize(asPerson)
|
||||
suite.NoError(err)
|
||||
|
||||
// Drop "@context" property as
|
||||
// the ordering is non-determinate.
|
||||
delete(ser, "@context")
|
||||
|
||||
bytes, err := json.MarshalIndent(ser, "", " ")
|
||||
suite.NoError(err)
|
||||
|
||||
// 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,
|
||||
suite.Equal(`{
|
||||
"discoverable": 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",
|
||||
@@ -309,7 +310,7 @@ func (suite *InternalToASTestSuite) TestAccountToASWithEmoji() {
|
||||
},
|
||||
"type": "Person",
|
||||
"url": "http://localhost:8080/@the_mighty_zork"
|
||||
}`, trimmed)
|
||||
}`, string(bytes))
|
||||
}
|
||||
|
||||
func (suite *InternalToASTestSuite) TestAccountToASWithSharedInbox() {
|
||||
@@ -324,14 +325,15 @@ func (suite *InternalToASTestSuite) TestAccountToASWithSharedInbox() {
|
||||
ser, err := ap.Serialize(asPerson)
|
||||
suite.NoError(err)
|
||||
|
||||
// Drop "@context" property as
|
||||
// the ordering is non-determinate.
|
||||
delete(ser, "@context")
|
||||
|
||||
bytes, err := json.MarshalIndent(ser, "", " ")
|
||||
suite.NoError(err)
|
||||
|
||||
// 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,
|
||||
suite.Equal(`{
|
||||
"discoverable": true,
|
||||
"endpoints": {
|
||||
"sharedInbox": "http://localhost:8080/sharedInbox"
|
||||
},
|
||||
@@ -363,7 +365,7 @@ func (suite *InternalToASTestSuite) TestAccountToASWithSharedInbox() {
|
||||
"tag": [],
|
||||
"type": "Person",
|
||||
"url": "http://localhost:8080/@the_mighty_zork"
|
||||
}`, trimmed)
|
||||
}`, string(bytes))
|
||||
}
|
||||
|
||||
func (suite *InternalToASTestSuite) TestStatusToAS() {
|
||||
@@ -376,11 +378,14 @@ func (suite *InternalToASTestSuite) TestStatusToAS() {
|
||||
ser, err := ap.Serialize(asStatus)
|
||||
suite.NoError(err)
|
||||
|
||||
// Drop "@context" property as
|
||||
// the ordering is non-determinate.
|
||||
delete(ser, "@context")
|
||||
|
||||
bytes, err := json.MarshalIndent(ser, "", " ")
|
||||
suite.NoError(err)
|
||||
|
||||
suite.Equal(`{
|
||||
"@context": "https://www.w3.org/ns/activitystreams",
|
||||
"attachment": [],
|
||||
"attributedTo": "http://localhost:8080/users/the_mighty_zork",
|
||||
"cc": "http://localhost:8080/users/the_mighty_zork/followers",
|
||||
@@ -389,6 +394,26 @@ func (suite *InternalToASTestSuite) TestStatusToAS() {
|
||||
"en": "hello everyone!"
|
||||
},
|
||||
"id": "http://localhost:8080/users/the_mighty_zork/statuses/01F8MHAMCHF6Y650WCRSCP4WMY",
|
||||
"interactionPolicy": {
|
||||
"canAnnounce": {
|
||||
"always": [
|
||||
"https://www.w3.org/ns/activitystreams#Public"
|
||||
],
|
||||
"approvalRequired": []
|
||||
},
|
||||
"canLike": {
|
||||
"always": [
|
||||
"https://www.w3.org/ns/activitystreams#Public"
|
||||
],
|
||||
"approvalRequired": []
|
||||
},
|
||||
"canReply": {
|
||||
"always": [
|
||||
"https://www.w3.org/ns/activitystreams#Public"
|
||||
],
|
||||
"approvalRequired": []
|
||||
}
|
||||
},
|
||||
"published": "2021-10-20T12:40:37+02:00",
|
||||
"replies": {
|
||||
"first": {
|
||||
@@ -420,14 +445,15 @@ func (suite *InternalToASTestSuite) TestStatusWithTagsToASWithIDs() {
|
||||
ser, err := ap.Serialize(asStatus)
|
||||
suite.NoError(err)
|
||||
|
||||
// Drop "@context" property as
|
||||
// the ordering is non-determinate.
|
||||
delete(ser, "@context")
|
||||
|
||||
bytes, err := json.MarshalIndent(ser, "", " ")
|
||||
suite.NoError(err)
|
||||
|
||||
// we can't be sure in what order the two context entries --
|
||||
// http://joinmastodon.org/ns, https://www.w3.org/ns/activitystreams --
|
||||
// will appear, so trim them out of the string for consistency
|
||||
trimmed := strings.SplitAfter(string(bytes), `"attachment":`)[1]
|
||||
suite.Equal(` [
|
||||
suite.Equal(`{
|
||||
"attachment": [
|
||||
{
|
||||
"blurhash": "LIIE|gRj00WB-;j[t7j[4nWBj[Rj",
|
||||
"mediaType": "image/jpeg",
|
||||
@@ -443,6 +469,26 @@ func (suite *InternalToASTestSuite) TestStatusWithTagsToASWithIDs() {
|
||||
"en": "hello world! #welcome ! first post on the instance :rainbow: !"
|
||||
},
|
||||
"id": "http://localhost:8080/users/admin/statuses/01F8MH75CBF9JFX4ZAD54N0W0R",
|
||||
"interactionPolicy": {
|
||||
"canAnnounce": {
|
||||
"always": [
|
||||
"https://www.w3.org/ns/activitystreams#Public"
|
||||
],
|
||||
"approvalRequired": []
|
||||
},
|
||||
"canLike": {
|
||||
"always": [
|
||||
"https://www.w3.org/ns/activitystreams#Public"
|
||||
],
|
||||
"approvalRequired": []
|
||||
},
|
||||
"canReply": {
|
||||
"always": [
|
||||
"https://www.w3.org/ns/activitystreams#Public"
|
||||
],
|
||||
"approvalRequired": []
|
||||
}
|
||||
},
|
||||
"published": "2021-10-20T11:36:45Z",
|
||||
"replies": {
|
||||
"first": {
|
||||
@@ -477,7 +523,7 @@ func (suite *InternalToASTestSuite) TestStatusWithTagsToASWithIDs() {
|
||||
"to": "https://www.w3.org/ns/activitystreams#Public",
|
||||
"type": "Note",
|
||||
"url": "http://localhost:8080/@admin/statuses/01F8MH75CBF9JFX4ZAD54N0W0R"
|
||||
}`, trimmed)
|
||||
}`, string(bytes))
|
||||
}
|
||||
|
||||
func (suite *InternalToASTestSuite) TestStatusWithTagsToASFromDB() {
|
||||
@@ -492,14 +538,15 @@ func (suite *InternalToASTestSuite) TestStatusWithTagsToASFromDB() {
|
||||
ser, err := ap.Serialize(asStatus)
|
||||
suite.NoError(err)
|
||||
|
||||
// Drop "@context" property as
|
||||
// the ordering is non-determinate.
|
||||
delete(ser, "@context")
|
||||
|
||||
bytes, err := json.MarshalIndent(ser, "", " ")
|
||||
suite.NoError(err)
|
||||
|
||||
// we can't be sure in what order the two context entries --
|
||||
// http://joinmastodon.org/ns, https://www.w3.org/ns/activitystreams --
|
||||
// will appear, so trim them out of the string for consistency
|
||||
trimmed := strings.SplitAfter(string(bytes), `"attachment":`)[1]
|
||||
suite.Equal(` [
|
||||
suite.Equal(`{
|
||||
"attachment": [
|
||||
{
|
||||
"blurhash": "LIIE|gRj00WB-;j[t7j[4nWBj[Rj",
|
||||
"mediaType": "image/jpeg",
|
||||
@@ -515,6 +562,26 @@ func (suite *InternalToASTestSuite) TestStatusWithTagsToASFromDB() {
|
||||
"en": "hello world! #welcome ! first post on the instance :rainbow: !"
|
||||
},
|
||||
"id": "http://localhost:8080/users/admin/statuses/01F8MH75CBF9JFX4ZAD54N0W0R",
|
||||
"interactionPolicy": {
|
||||
"canAnnounce": {
|
||||
"always": [
|
||||
"https://www.w3.org/ns/activitystreams#Public"
|
||||
],
|
||||
"approvalRequired": []
|
||||
},
|
||||
"canLike": {
|
||||
"always": [
|
||||
"https://www.w3.org/ns/activitystreams#Public"
|
||||
],
|
||||
"approvalRequired": []
|
||||
},
|
||||
"canReply": {
|
||||
"always": [
|
||||
"https://www.w3.org/ns/activitystreams#Public"
|
||||
],
|
||||
"approvalRequired": []
|
||||
}
|
||||
},
|
||||
"published": "2021-10-20T11:36:45Z",
|
||||
"replies": {
|
||||
"first": {
|
||||
@@ -549,7 +616,7 @@ func (suite *InternalToASTestSuite) TestStatusWithTagsToASFromDB() {
|
||||
"to": "https://www.w3.org/ns/activitystreams#Public",
|
||||
"type": "Note",
|
||||
"url": "http://localhost:8080/@admin/statuses/01F8MH75CBF9JFX4ZAD54N0W0R"
|
||||
}`, trimmed)
|
||||
}`, string(bytes))
|
||||
}
|
||||
|
||||
func (suite *InternalToASTestSuite) TestStatusToASWithMentions() {
|
||||
@@ -565,11 +632,14 @@ func (suite *InternalToASTestSuite) TestStatusToASWithMentions() {
|
||||
ser, err := ap.Serialize(asStatus)
|
||||
suite.NoError(err)
|
||||
|
||||
// Drop "@context" property as
|
||||
// the ordering is non-determinate.
|
||||
delete(ser, "@context")
|
||||
|
||||
bytes, err := json.MarshalIndent(ser, "", " ")
|
||||
suite.NoError(err)
|
||||
|
||||
suite.Equal(`{
|
||||
"@context": "https://www.w3.org/ns/activitystreams",
|
||||
"attachment": [],
|
||||
"attributedTo": "http://localhost:8080/users/admin",
|
||||
"cc": [
|
||||
@@ -582,6 +652,26 @@ func (suite *InternalToASTestSuite) TestStatusToASWithMentions() {
|
||||
},
|
||||
"id": "http://localhost:8080/users/admin/statuses/01FF25D5Q0DH7CHD57CTRS6WK0",
|
||||
"inReplyTo": "http://localhost:8080/users/the_mighty_zork/statuses/01F8MHAMCHF6Y650WCRSCP4WMY",
|
||||
"interactionPolicy": {
|
||||
"canAnnounce": {
|
||||
"always": [
|
||||
"https://www.w3.org/ns/activitystreams#Public"
|
||||
],
|
||||
"approvalRequired": []
|
||||
},
|
||||
"canLike": {
|
||||
"always": [
|
||||
"https://www.w3.org/ns/activitystreams#Public"
|
||||
],
|
||||
"approvalRequired": []
|
||||
},
|
||||
"canReply": {
|
||||
"always": [
|
||||
"https://www.w3.org/ns/activitystreams#Public"
|
||||
],
|
||||
"approvalRequired": []
|
||||
}
|
||||
},
|
||||
"published": "2021-11-20T13:32:16Z",
|
||||
"replies": {
|
||||
"first": {
|
||||
@@ -967,6 +1057,51 @@ func (suite *InternalToASTestSuite) TestPollVoteToASCreate() {
|
||||
}`, string(bytes))
|
||||
}
|
||||
|
||||
func (suite *InternalToASTestSuite) TestInteractionApprovalToASAccept() {
|
||||
acceptingAccount := suite.testAccounts["local_account_1"]
|
||||
interactingAccount := suite.testAccounts["remote_account_1"]
|
||||
|
||||
interactionApproval := >smodel.InteractionApproval{
|
||||
ID: "01J1AKMZ8JE5NW0ZSFTRC1JJNE",
|
||||
CreatedAt: testrig.TimeMustParse("2022-06-09T13:12:00Z"),
|
||||
UpdatedAt: testrig.TimeMustParse("2022-06-09T13:12:00Z"),
|
||||
AccountID: acceptingAccount.ID,
|
||||
Account: acceptingAccount,
|
||||
InteractingAccountID: interactingAccount.ID,
|
||||
InteractingAccount: interactingAccount,
|
||||
InteractionURI: "https://fossbros-anonymous.io/users/foss_satan/statuses/01J1AKRRHQ6MDDQHV0TP716T2K",
|
||||
InteractionType: gtsmodel.InteractionAnnounce,
|
||||
URI: "http://localhost:8080/users/the_mighty_zork/accepts/01J1AKMZ8JE5NW0ZSFTRC1JJNE",
|
||||
}
|
||||
|
||||
accept, err := suite.typeconverter.InteractionApprovalToASAccept(
|
||||
context.Background(),
|
||||
interactionApproval,
|
||||
)
|
||||
if err != nil {
|
||||
suite.FailNow(err.Error())
|
||||
}
|
||||
|
||||
i, err := ap.Serialize(accept)
|
||||
if err != nil {
|
||||
suite.FailNow(err.Error())
|
||||
}
|
||||
|
||||
b, err := json.MarshalIndent(i, "", " ")
|
||||
if err != nil {
|
||||
suite.FailNow(err.Error())
|
||||
}
|
||||
|
||||
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/accepts/01J1AKMZ8JE5NW0ZSFTRC1JJNE",
|
||||
"object": "https://fossbros-anonymous.io/users/foss_satan/statuses/01J1AKRRHQ6MDDQHV0TP716T2K",
|
||||
"to": "http://fossbros-anonymous.io/users/foss_satan",
|
||||
"type": "Accept"
|
||||
}`, string(b))
|
||||
}
|
||||
|
||||
func TestInternalToASTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(InternalToASTestSuite))
|
||||
}
|
||||
|
@@ -72,11 +72,14 @@ func (suite *WrapTestSuite) TestWrapNoteInCreate() {
|
||||
createI, err := ap.Serialize(create)
|
||||
suite.NoError(err)
|
||||
|
||||
// Chop off @context since
|
||||
// ordering is non-determinate.
|
||||
delete(createI, "@context")
|
||||
|
||||
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",
|
||||
"cc": "http://localhost:8080/users/the_mighty_zork/followers",
|
||||
"id": "http://localhost:8080/users/the_mighty_zork/statuses/01F8MHAMCHF6Y650WCRSCP4WMY/activity#Create",
|
||||
@@ -89,6 +92,26 @@ func (suite *WrapTestSuite) TestWrapNoteInCreate() {
|
||||
"en": "hello everyone!"
|
||||
},
|
||||
"id": "http://localhost:8080/users/the_mighty_zork/statuses/01F8MHAMCHF6Y650WCRSCP4WMY",
|
||||
"interactionPolicy": {
|
||||
"canAnnounce": {
|
||||
"always": [
|
||||
"https://www.w3.org/ns/activitystreams#Public"
|
||||
],
|
||||
"approvalRequired": []
|
||||
},
|
||||
"canLike": {
|
||||
"always": [
|
||||
"https://www.w3.org/ns/activitystreams#Public"
|
||||
],
|
||||
"approvalRequired": []
|
||||
},
|
||||
"canReply": {
|
||||
"always": [
|
||||
"https://www.w3.org/ns/activitystreams#Public"
|
||||
],
|
||||
"approvalRequired": []
|
||||
}
|
||||
},
|
||||
"published": "2021-10-20T12:40:37+02:00",
|
||||
"replies": {
|
||||
"first": {
|
||||
|
Reference in New Issue
Block a user