summaryrefslogtreecommitdiff
path: root/internal/typeutils
diff options
context:
space:
mode:
authorLibravatar tobi <31960611+tsmethurst@users.noreply.github.com>2024-07-26 12:04:28 +0200
committerLibravatar GitHub <noreply@github.com>2024-07-26 12:04:28 +0200
commit8ab2b19a946251f258446d22f420d401f61d22f6 (patch)
tree39fb674f135fd1cfcf4de5b319913f0d0c17d11a /internal/typeutils
parent[docs] Add separate migration section + instructions for moving to GtS and no... (diff)
downloadgotosocial-8ab2b19a946251f258446d22f420d401f61d22f6.tar.xz
[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
Diffstat (limited to 'internal/typeutils')
-rw-r--r--internal/typeutils/astointernal.go20
-rw-r--r--internal/typeutils/internaltoas.go281
-rw-r--r--internal/typeutils/internaltoas_test.go243
-rw-r--r--internal/typeutils/wrap_test.go25
4 files changed, 509 insertions, 60 deletions
diff --git a/internal/typeutils/astointernal.go b/internal/typeutils/astointernal.go
index cb3e320d9..2946c8d09 100644
--- a/internal/typeutils/astointernal.go
+++ b/internal/typeutils/astointernal.go
@@ -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
diff --git a/internal/typeutils/internaltoas.go b/internal/typeutils/internaltoas.go
index 567493673..31b256b6c 100644
--- a/internal/typeutils/internaltoas.go
+++ b/internal/typeutils/internaltoas.go
@@ -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
+}
diff --git a/internal/typeutils/internaltoas_test.go b/internal/typeutils/internaltoas_test.go
index ca8143436..905dccfad 100644
--- a/internal/typeutils/internaltoas_test.go
+++ b/internal/typeutils/internaltoas_test.go
@@ -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 := &gtsmodel.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))
}
diff --git a/internal/typeutils/wrap_test.go b/internal/typeutils/wrap_test.go
index 453073ed6..833b18bac 100644
--- a/internal/typeutils/wrap_test.go
+++ b/internal/typeutils/wrap_test.go
@@ -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": {