incoming UNDO for follows now working

This commit is contained in:
tsmethurst 2021-05-18 23:06:20 +02:00
parent 8832784778
commit c7b4f847d8
18 changed files with 265 additions and 110 deletions

View File

@ -30,7 +30,7 @@
* [ ] /api/v1/accounts/:id/pin POST (Feature this account on profile)
* [ ] /api/v1/accounts/:id/unpin POST (Remove this account from profile)
* [ ] /api/v1/accounts/:id/note POST (Make a personal note about this account)
* [ ] /api/v1/accounts/relationships GET (Check relationships with accounts)
* [x] /api/v1/accounts/relationships GET (Check relationships with accounts)
* [ ] /api/v1/accounts/search GET (Search for an account)
* [ ] Bookmarks
* [ ] /api/v1/bookmarks GET (See bookmarked statuses)
@ -177,6 +177,7 @@
* [ ] 'Greedy' federation
* [ ] No federation (insulate this instance from the Fediverse)
* [ ] Allowlist
* [x] Secure HTTP signatures (creation and validation)
* [ ] Storage
* [x] Internal/statuses/preferences etc
* [x] Postgres interface

View File

@ -22,7 +22,6 @@ import (
"context"
"net"
"github.com/go-fed/activity/pub"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
)
@ -44,7 +43,7 @@ func (e ErrNoEntries) Error() string {
type DB interface {
// Federation returns an interface that's compatible with go-fed, for performing federation storage/retrieval functions.
// See: https://pkg.go.dev/github.com/go-fed/activity@v1.0.0/pub?utm_source=gopls#Database
Federation() pub.Database
// Federation() federatingdb.FederatingDB
/*
BASIC DB FUNCTIONALITY

View File

@ -30,7 +30,6 @@ import (
"strings"
"time"
"github.com/go-fed/activity/pub"
"github.com/go-pg/pg/extra/pgdebug"
"github.com/go-pg/pg/v10"
"github.com/go-pg/pg/v10/orm"
@ -38,7 +37,6 @@ import (
"github.com/sirupsen/logrus"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/federation"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/util"
"golang.org/x/crypto/bcrypt"
@ -46,11 +44,11 @@ import (
// postgresService satisfies the DB interface
type postgresService struct {
config *config.Config
conn *pg.DB
log *logrus.Logger
cancel context.CancelFunc
federationDB pub.Database
config *config.Config
conn *pg.DB
log *logrus.Logger
cancel context.CancelFunc
// federationDB pub.Database
}
// NewPostgresService returns a postgresService derived from the provided config, which implements the go-fed DB interface.
@ -97,9 +95,6 @@ func NewPostgresService(ctx context.Context, c *config.Config, log *logrus.Logge
cancel: cancel,
}
federatingDB := federation.NewFederatingDB(ps, c, log)
ps.federationDB = federatingDB
// we can confidently return this useable postgres service now
return ps, nil
}
@ -159,14 +154,6 @@ func derivePGOptions(c *config.Config) (*pg.Options, error) {
return options, nil
}
/*
FEDERATION FUNCTIONALITY
*/
func (ps *postgresService) Federation() pub.Database {
return ps.federationDB
}
/*
BASIC DB FUNCTIONALITY
*/
@ -285,20 +272,22 @@ func (ps *postgresService) UpdateOneByID(id string, key string, value interface{
func (ps *postgresService) DeleteByID(id string, i interface{}) error {
if _, err := ps.conn.Model(i).Where("id = ?", id).Delete(); err != nil {
if err == pg.ErrNoRows {
return db.ErrNoEntries{}
// if there are no rows *anyway* then that's fine
// just return err if there's an actual error
if err != pg.ErrNoRows {
return err
}
return err
}
return nil
}
func (ps *postgresService) DeleteWhere(key string, value interface{}, i interface{}) error {
if _, err := ps.conn.Model(i).Where("? = ?", pg.Safe(key), value).Delete(); err != nil {
if err == pg.ErrNoRows {
return db.ErrNoEntries{}
// if there are no rows *anyway* then that's fine
// just return err if there's an actual error
if err != pg.ErrNoRows {
return err
}
return err
}
return nil
}

View File

@ -38,6 +38,11 @@ import (
"github.com/superseriousbusiness/gotosocial/internal/util"
)
type FederatingDB interface {
pub.Database
Undo(c context.Context, asType vocab.Type) error
}
// FederatingDB uses the underlying DB interface to implement the go-fed pub.Database interface.
// It doesn't care what the underlying implementation of the DB interface is, as long as it works.
type federatingDB struct {
@ -48,8 +53,8 @@ type federatingDB struct {
typeConverter typeutils.TypeConverter
}
// NewFederatingDB returns a pub.Database interface using the given database, config, and logger.
func NewFederatingDB(db db.DB, config *config.Config, log *logrus.Logger) pub.Database {
// NewFederatingDB returns a FederatingDB interface using the given database, config, and logger.
func NewFederatingDB(db db.DB, config *config.Config, log *logrus.Logger) FederatingDB {
return &federatingDB{
locks: new(sync.Map),
db: db,
@ -405,7 +410,7 @@ func (f *federatingDB) Create(ctx context.Context, asType vocab.Type) error {
return nil
}
switch gtsmodel.ActivityStreamsActivity(asType.GetTypeName()) {
switch asType.GetTypeName() {
case gtsmodel.ActivityStreamsCreate:
create, ok := asType.(vocab.ActivityStreamsCreate)
if !ok {
@ -413,7 +418,7 @@ func (f *federatingDB) Create(ctx context.Context, asType vocab.Type) error {
}
object := create.GetActivityStreamsObject()
for objectIter := object.Begin(); objectIter != object.End(); objectIter = objectIter.Next() {
switch gtsmodel.ActivityStreamsObject(objectIter.GetType().GetTypeName()) {
switch objectIter.GetType().GetTypeName() {
case gtsmodel.ActivityStreamsNote:
note := objectIter.GetActivityStreamsNote()
status, err := f.typeConverter.ASStatusToStatus(note)
@ -425,9 +430,10 @@ func (f *federatingDB) Create(ctx context.Context, asType vocab.Type) error {
}
fromFederatorChan <- gtsmodel.FromFederator{
APObjectType: gtsmodel.ActivityStreamsNote,
APActivityType: gtsmodel.ActivityStreamsCreate,
GTSModel: status,
APObjectType: gtsmodel.ActivityStreamsNote,
APActivityType: gtsmodel.ActivityStreamsCreate,
GTSModel: status,
ReceivingAccount: targetAcct,
}
}
}
@ -455,6 +461,98 @@ func (f *federatingDB) Create(ctx context.Context, asType vocab.Type) error {
return nil
}
func (f *federatingDB) Undo(ctx context.Context, asType vocab.Type) error {
l := f.log.WithFields(
logrus.Fields{
"func": "Undo",
"asType": asType.GetTypeName(),
},
)
m, err := streams.Serialize(asType)
if err != nil {
return err
}
b, err := json.Marshal(m)
if err != nil {
return err
}
l.Debugf("received UNDO asType %s", string(b))
targetAcctI := ctx.Value(util.APAccount)
if targetAcctI == nil {
l.Error("UNDO: target account wasn't set on context")
return nil
}
targetAcct, ok := targetAcctI.(*gtsmodel.Account)
if !ok {
l.Error("UNDO: target account was set on context but couldn't be parsed")
return nil
}
// fromFederatorChanI := ctx.Value(util.APFromFederatorChanKey)
// if fromFederatorChanI == nil {
// l.Error("from federator channel wasn't set on context")
// return nil
// }
// fromFederatorChan, ok := fromFederatorChanI.(chan gtsmodel.FromFederator)
// if !ok {
// l.Error("from federator channel was set on context but couldn't be parsed")
// return nil
// }
switch asType.GetTypeName() {
// UNDO
case gtsmodel.ActivityStreamsUndo:
undo, ok := asType.(vocab.ActivityStreamsUndo)
if !ok {
return errors.New("UNDO: couldn't parse UNDO into vocab.ActivityStreamsUndo")
}
undoObject := undo.GetActivityStreamsObject()
if undoObject == nil {
return errors.New("UNDO: no object set on vocab.ActivityStreamsUndo")
}
for iter := undoObject.Begin(); iter != undoObject.End(); iter = iter.Next() {
switch iter.GetType().GetTypeName() {
case string(gtsmodel.ActivityStreamsFollow):
// UNDO FOLLOW
ASFollow, ok := iter.GetType().(vocab.ActivityStreamsFollow)
if !ok {
return errors.New("UNDO: couldn't parse follow into vocab.ActivityStreamsFollow")
}
// make sure the actor owns the follow
if !sameActor(undo.GetActivityStreamsActor(), ASFollow.GetActivityStreamsActor()) {
return errors.New("UNDO: follow actor and activity actor not the same")
}
// convert the follow to something we can understand
gtsFollow, err := f.typeConverter.ASFollowToFollow(ASFollow)
if err != nil {
return fmt.Errorf("UNDO: error converting asfollow to gtsfollow: %s", err)
}
// make sure the addressee of the original follow is the same as whatever inbox this landed in
if gtsFollow.TargetAccountID != targetAcct.ID {
return errors.New("UNDO: follow object account and inbox account were not the same")
}
// delete any existing FOLLOW
if err := f.db.DeleteWhere("uri", gtsFollow.URI, &gtsmodel.Follow{}); err != nil {
return fmt.Errorf("UNDO: db error removing follow: %s", err)
}
// delete any existing FOLLOW REQUEST
if err := f.db.DeleteWhere("uri", gtsFollow.URI, &gtsmodel.FollowRequest{}); err != nil {
return fmt.Errorf("UNDO: db error removing follow request: %s", err)
}
l.Debug("follow undone")
return nil
case string(gtsmodel.ActivityStreamsLike):
// UNDO LIKE
case string(gtsmodel.ActivityStreamsAnnounce):
// UNDO BOOST/REBLOG/ANNOUNCE
}
}
}
return nil
}
// Update sets an existing entry to the database based on the value's
// id.
//
@ -500,7 +598,7 @@ func (f *federatingDB) Update(ctx context.Context, asType vocab.Type) error {
l.Error("from federator channel was set on context but couldn't be parsed")
}
switch gtsmodel.ActivityStreamsActivity(asType.GetTypeName()) {
switch asType.GetTypeName() {
case gtsmodel.ActivityStreamsUpdate:
update, ok := asType.(vocab.ActivityStreamsCreate)
if !ok {

View File

@ -20,14 +20,12 @@ package federation
import (
"context"
"encoding/json"
"errors"
"fmt"
"net/http"
"net/url"
"github.com/go-fed/activity/pub"
"github.com/go-fed/activity/streams"
"github.com/go-fed/activity/streams/vocab"
"github.com/sirupsen/logrus"
"github.com/superseriousbusiness/gotosocial/internal/db"
@ -271,7 +269,7 @@ func (f *federator) FederatingCallbacks(ctx context.Context) (wrapped pub.Federa
// override default undo behavior
other = []interface{}{
func(ctx context.Context, undo vocab.ActivityStreamsUndo) error {
return f.typeConverter.
return f.FederatingDB().Undo(ctx, undo)
},
}

View File

@ -52,6 +52,7 @@ type Federator interface {
type federator struct {
config *config.Config
db db.DB
federatingDB FederatingDB
clock pub.Clock
typeConverter typeutils.TypeConverter
transportController transport.Controller
@ -60,18 +61,19 @@ type federator struct {
}
// NewFederator returns a new federator
func NewFederator(db db.DB, transportController transport.Controller, config *config.Config, log *logrus.Logger, typeConverter typeutils.TypeConverter) Federator {
func NewFederator(db db.DB, federatingDB FederatingDB, transportController transport.Controller, config *config.Config, log *logrus.Logger, typeConverter typeutils.TypeConverter) Federator {
clock := &Clock{}
f := &federator{
config: config,
db: db,
federatingDB: federatingDB,
clock: &Clock{},
typeConverter: typeConverter,
transportController: transportController,
log: log,
}
actor := newFederatingActor(f, f, db.Federation(), clock)
actor := newFederatingActor(f, f, federatingDB, clock)
f.actor = actor
return f
}
@ -79,3 +81,7 @@ func NewFederator(db db.DB, transportController transport.Controller, config *co
func (f *federator) FederatingActor() pub.FederatingActor {
return f.actor
}
func (f *federator) FederatingDB() FederatingDB {
return f.federatingDB
}

View File

@ -89,7 +89,7 @@ func (suite *ProtocolTestSuite) TestPostInboxRequestBodyHook() {
return nil, nil
}))
// setup module being tested
federator := federation.NewFederator(suite.db, tc, suite.config, suite.log, suite.typeConverter)
federator := federation.NewFederator(suite.db, testrig.NewTestFederatingDB(suite.db), tc, suite.config, suite.log, suite.typeConverter)
// setup request
ctx := context.Background()
@ -155,7 +155,7 @@ func (suite *ProtocolTestSuite) TestAuthenticatePostInbox() {
}))
// now setup module being tested, with the mock transport controller
federator := federation.NewFederator(suite.db, tc, suite.config, suite.log, suite.typeConverter)
federator := federation.NewFederator(suite.db, testrig.NewTestFederatingDB(suite.db), tc, suite.config, suite.log, suite.typeConverter)
// setup request
ctx := context.Background()

View File

@ -243,3 +243,23 @@ func (f *federator) GetTransportForUser(username string) (transport.Transport, e
}
return transport, nil
}
func sameActor(activityActor vocab.ActivityStreamsActorProperty, followActor vocab.ActivityStreamsActorProperty) bool {
if activityActor == nil || followActor == nil {
return false
}
for aIter := activityActor.Begin(); aIter != activityActor.End(); aIter = aIter.Next() {
for fIter := followActor.Begin(); fIter != followActor.End(); fIter = fIter.Next() {
if aIter.GetIRI() == nil {
return false
}
if fIter.GetIRI() == nil {
return false
}
if aIter.GetIRI().String() == fIter.GetIRI().String() {
return true
}
}
}
return false
}

View File

@ -83,6 +83,8 @@ var Run action.GTSAction = func(ctx context.Context, c *config.Config, log *logr
return fmt.Errorf("error creating dbservice: %s", err)
}
federatingDB := federation.NewFederatingDB(dbService, c, log)
router, err := router.New(c, log)
if err != nil {
return fmt.Errorf("error creating router: %s", err)
@ -100,7 +102,7 @@ var Run action.GTSAction = func(ctx context.Context, c *config.Config, log *logr
mediaHandler := media.New(c, dbService, storageBackend, log)
oauthServer := oauth.New(dbService, log)
transportController := transport.NewController(c, &federation.Clock{}, http.DefaultClient, log)
federator := federation.NewFederator(dbService, transportController, c, log, typeConverter)
federator := federation.NewFederator(dbService, federatingDB, transportController, c, log, typeConverter)
processor := message.NewProcessor(c, typeConverter, federator, oauthServer, mediaHandler, storageBackend, dbService, log)
if err := processor.Start(); err != nil {
return fmt.Errorf("error starting processor: %s", err)

View File

@ -107,7 +107,7 @@ type Account struct {
// URL for getting the featured collection list of this account
FeaturedCollectionURI string `pg:",unique"`
// What type of activitypub actor is this account?
ActorType ActivityStreamsActor
ActorType string
// This account is associated with x account id
AlsoKnownAs string

View File

@ -18,110 +18,101 @@
package gtsmodel
// ActivityStreamsObject refers to https://www.w3.org/TR/activitystreams-vocabulary/#object-types
type ActivityStreamsObject string
const (
// ActivityStreamsArticle https://www.w3.org/TR/activitystreams-vocabulary/#dfn-article
ActivityStreamsArticle ActivityStreamsObject = "Article"
ActivityStreamsArticle = "Article"
// ActivityStreamsAudio https://www.w3.org/TR/activitystreams-vocabulary/#dfn-audio
ActivityStreamsAudio ActivityStreamsObject = "Audio"
ActivityStreamsAudio = "Audio"
// ActivityStreamsDocument https://www.w3.org/TR/activitystreams-vocabulary/#dfn-document
ActivityStreamsDocument ActivityStreamsObject = "Event"
ActivityStreamsDocument = "Event"
// ActivityStreamsEvent https://www.w3.org/TR/activitystreams-vocabulary/#dfn-event
ActivityStreamsEvent ActivityStreamsObject = "Event"
ActivityStreamsEvent = "Event"
// ActivityStreamsImage https://www.w3.org/TR/activitystreams-vocabulary/#dfn-image
ActivityStreamsImage ActivityStreamsObject = "Image"
ActivityStreamsImage = "Image"
// ActivityStreamsNote https://www.w3.org/TR/activitystreams-vocabulary/#dfn-note
ActivityStreamsNote ActivityStreamsObject = "Note"
ActivityStreamsNote = "Note"
// ActivityStreamsPage https://www.w3.org/TR/activitystreams-vocabulary/#dfn-page
ActivityStreamsPage ActivityStreamsObject = "Page"
ActivityStreamsPage = "Page"
// ActivityStreamsPlace https://www.w3.org/TR/activitystreams-vocabulary/#dfn-place
ActivityStreamsPlace ActivityStreamsObject = "Place"
ActivityStreamsPlace = "Place"
// ActivityStreamsProfile https://www.w3.org/TR/activitystreams-vocabulary/#dfn-profile
ActivityStreamsProfile ActivityStreamsObject = "Profile"
ActivityStreamsProfile = "Profile"
// ActivityStreamsRelationship https://www.w3.org/TR/activitystreams-vocabulary/#dfn-relationship
ActivityStreamsRelationship ActivityStreamsObject = "Relationship"
ActivityStreamsRelationship = "Relationship"
// ActivityStreamsTombstone https://www.w3.org/TR/activitystreams-vocabulary/#dfn-tombstone
ActivityStreamsTombstone ActivityStreamsObject = "Tombstone"
ActivityStreamsTombstone = "Tombstone"
// ActivityStreamsVideo https://www.w3.org/TR/activitystreams-vocabulary/#dfn-video
ActivityStreamsVideo ActivityStreamsObject = "Video"
ActivityStreamsVideo = "Video"
)
// ActivityStreamsActor refers to https://www.w3.org/TR/activitystreams-vocabulary/#actor-types
type ActivityStreamsActor string
const (
// ActivityStreamsApplication https://www.w3.org/TR/activitystreams-vocabulary/#dfn-application
ActivityStreamsApplication ActivityStreamsActor = "Application"
ActivityStreamsApplication = "Application"
// ActivityStreamsGroup https://www.w3.org/TR/activitystreams-vocabulary/#dfn-group
ActivityStreamsGroup ActivityStreamsActor = "Group"
ActivityStreamsGroup = "Group"
// ActivityStreamsOrganization https://www.w3.org/TR/activitystreams-vocabulary/#dfn-organization
ActivityStreamsOrganization ActivityStreamsActor = "Organization"
ActivityStreamsOrganization = "Organization"
// ActivityStreamsPerson https://www.w3.org/TR/activitystreams-vocabulary/#dfn-person
ActivityStreamsPerson ActivityStreamsActor = "Person"
ActivityStreamsPerson = "Person"
// ActivityStreamsService https://www.w3.org/TR/activitystreams-vocabulary/#dfn-service
ActivityStreamsService ActivityStreamsActor = "Service"
ActivityStreamsService = "Service"
)
// ActivityStreamsActivity refers to https://www.w3.org/TR/activitystreams-vocabulary/#activity-types
type ActivityStreamsActivity string
const (
// ActivityStreamsAccept https://www.w3.org/TR/activitystreams-vocabulary/#dfn-accept
ActivityStreamsAccept ActivityStreamsActivity = "Accept"
ActivityStreamsAccept = "Accept"
// ActivityStreamsAdd https://www.w3.org/TR/activitystreams-vocabulary/#dfn-add
ActivityStreamsAdd ActivityStreamsActivity = "Add"
ActivityStreamsAdd = "Add"
// ActivityStreamsAnnounce https://www.w3.org/TR/activitystreams-vocabulary/#dfn-announce
ActivityStreamsAnnounce ActivityStreamsActivity = "Announce"
ActivityStreamsAnnounce = "Announce"
// ActivityStreamsArrive https://www.w3.org/TR/activitystreams-vocabulary/#dfn-arrive
ActivityStreamsArrive ActivityStreamsActivity = "Arrive"
ActivityStreamsArrive = "Arrive"
// ActivityStreamsBlock https://www.w3.org/TR/activitystreams-vocabulary/#dfn-block
ActivityStreamsBlock ActivityStreamsActivity = "Block"
ActivityStreamsBlock = "Block"
// ActivityStreamsCreate https://www.w3.org/TR/activitystreams-vocabulary/#dfn-create
ActivityStreamsCreate ActivityStreamsActivity = "Create"
ActivityStreamsCreate = "Create"
// ActivityStreamsDelete https://www.w3.org/TR/activitystreams-vocabulary/#dfn-delete
ActivityStreamsDelete ActivityStreamsActivity = "Delete"
ActivityStreamsDelete = "Delete"
// ActivityStreamsDislike https://www.w3.org/TR/activitystreams-vocabulary/#dfn-dislike
ActivityStreamsDislike ActivityStreamsActivity = "Dislike"
ActivityStreamsDislike = "Dislike"
// ActivityStreamsFlag https://www.w3.org/TR/activitystreams-vocabulary/#dfn-flag
ActivityStreamsFlag ActivityStreamsActivity = "Flag"
ActivityStreamsFlag = "Flag"
// ActivityStreamsFollow https://www.w3.org/TR/activitystreams-vocabulary/#dfn-follow
ActivityStreamsFollow ActivityStreamsActivity = "Follow"
ActivityStreamsFollow = "Follow"
// ActivityStreamsIgnore https://www.w3.org/TR/activitystreams-vocabulary/#dfn-ignore
ActivityStreamsIgnore ActivityStreamsActivity = "Ignore"
ActivityStreamsIgnore = "Ignore"
// ActivityStreamsInvite https://www.w3.org/TR/activitystreams-vocabulary/#dfn-invite
ActivityStreamsInvite ActivityStreamsActivity = "Invite"
ActivityStreamsInvite = "Invite"
// ActivityStreamsJoin https://www.w3.org/TR/activitystreams-vocabulary/#dfn-join
ActivityStreamsJoin ActivityStreamsActivity = "Join"
ActivityStreamsJoin = "Join"
// ActivityStreamsLeave https://www.w3.org/TR/activitystreams-vocabulary/#dfn-leave
ActivityStreamsLeave ActivityStreamsActivity = "Leave"
ActivityStreamsLeave = "Leave"
// ActivityStreamsLike https://www.w3.org/TR/activitystreams-vocabulary/#dfn-like
ActivityStreamsLike ActivityStreamsActivity = "Like"
ActivityStreamsLike = "Like"
// ActivityStreamsListen https://www.w3.org/TR/activitystreams-vocabulary/#dfn-listen
ActivityStreamsListen ActivityStreamsActivity = "Listen"
ActivityStreamsListen = "Listen"
// ActivityStreamsMove https://www.w3.org/TR/activitystreams-vocabulary/#dfn-move
ActivityStreamsMove ActivityStreamsActivity = "Move"
ActivityStreamsMove = "Move"
// ActivityStreamsOffer https://www.w3.org/TR/activitystreams-vocabulary/#dfn-offer
ActivityStreamsOffer ActivityStreamsActivity = "Offer"
ActivityStreamsOffer = "Offer"
// ActivityStreamsQuestion https://www.w3.org/TR/activitystreams-vocabulary/#dfn-question
ActivityStreamsQuestion ActivityStreamsActivity = "Question"
ActivityStreamsQuestion = "Question"
// ActivityStreamsReject https://www.w3.org/TR/activitystreams-vocabulary/#dfn-reject
ActivityStreamsReject ActivityStreamsActivity = "Reject"
ActivityStreamsReject = "Reject"
// ActivityStreamsRead https://www.w3.org/TR/activitystreams-vocabulary/#dfn-read
ActivityStreamsRead ActivityStreamsActivity = "Read"
ActivityStreamsRead = "Read"
// ActivityStreamsRemove https://www.w3.org/TR/activitystreams-vocabulary/#dfn-remove
ActivityStreamsRemove ActivityStreamsActivity = "Remove"
ActivityStreamsRemove = "Remove"
// ActivityStreamsTentativeReject https://www.w3.org/TR/activitystreams-vocabulary/#dfn-tentativereject
ActivityStreamsTentativeReject ActivityStreamsActivity = "TentativeReject"
ActivityStreamsTentativeReject = "TentativeReject"
// ActivityStreamsTentativeAccept https://www.w3.org/TR/activitystreams-vocabulary/#dfn-tentativeaccept
ActivityStreamsTentativeAccept ActivityStreamsActivity = "TentativeAccept"
ActivityStreamsTentativeAccept = "TentativeAccept"
// ActivityStreamsTravel https://www.w3.org/TR/activitystreams-vocabulary/#dfn-travel
ActivityStreamsTravel ActivityStreamsActivity = "Travel"
ActivityStreamsTravel = "Travel"
// ActivityStreamsUndo https://www.w3.org/TR/activitystreams-vocabulary/#dfn-undo
ActivityStreamsUndo ActivityStreamsActivity = "Undo"
ActivityStreamsUndo = "Undo"
// ActivityStreamsUpdate https://www.w3.org/TR/activitystreams-vocabulary/#dfn-update
ActivityStreamsUpdate ActivityStreamsActivity = "Update"
ActivityStreamsUpdate = "Update"
// ActivityStreamsView https://www.w3.org/TR/activitystreams-vocabulary/#dfn-view
ActivityStreamsView ActivityStreamsActivity = "View"
ActivityStreamsView = "View"
)

View File

@ -9,8 +9,8 @@ package gtsmodel
// FromClientAPI wraps a message that travels from client API into the processor
type FromClientAPI struct {
APObjectType ActivityStreamsObject
APActivityType ActivityStreamsActivity
APObjectType string
APActivityType string
GTSModel interface{}
}
@ -23,8 +23,8 @@ type FromClientAPI struct {
// FromFederator wraps a message that travels from the federator into the processor
type FromFederator struct {
APObjectType ActivityStreamsObject
APActivityType ActivityStreamsActivity
APObjectType string
APActivityType string
GTSModel interface{}
ReceivingAccount *Account
}

View File

@ -66,7 +66,7 @@ type Status struct {
VisibilityAdvanced *VisibilityAdvanced
// What is the activitystreams type of this status? See: https://www.w3.org/TR/activitystreams-vocabulary/#object-types
// Will probably almost always be Note but who knows!.
ActivityStreamsType ActivityStreamsObject
ActivityStreamsType string
// Original text of the status without formatting
Text string
// Has this status been pinned by its owner?

View File

@ -90,7 +90,7 @@ func (c *converter) ASRepresentationToAccount(accountable Accountable) (*gtsmode
}
// check for bot and actor type
switch gtsmodel.ActivityStreamsActor(accountable.GetTypeName()) {
switch accountable.GetTypeName() {
case gtsmodel.ActivityStreamsPerson, gtsmodel.ActivityStreamsGroup, gtsmodel.ActivityStreamsOrganization:
// people, groups, and organizations aren't bots
acct.Bot = false
@ -101,7 +101,7 @@ func (c *converter) ASRepresentationToAccount(accountable Accountable) (*gtsmode
// we don't know what this is!
return nil, fmt.Errorf("type name %s not recognised or not convertible to gtsmodel.ActivityStreamsActor", accountable.GetTypeName())
}
acct.ActorType = gtsmodel.ActivityStreamsActor(accountable.GetTypeName())
acct.ActorType = accountable.GetTypeName()
// TODO: locked aka manuallyApprovesFollowers
@ -281,7 +281,10 @@ func (c *converter) ASStatusToStatus(statusable Statusable) (*gtsmodel.Status, e
// if it's CC'ed to public, it's public or unlocked
// mentioned SPECIFIC ACCOUNTS also get added to CC'es if it's not a direct message
if isPublic(cc) || isPublic(to) {
if isPublic(cc) {
visibility = gtsmodel.VisibilityUnlocked
}
if isPublic(to) {
visibility = gtsmodel.VisibilityPublic
}
@ -301,7 +304,7 @@ func (c *converter) ASStatusToStatus(statusable Statusable) (*gtsmodel.Status, e
// we might be able to extract this from the contentMap field
// ActivityStreamsType
status.ActivityStreamsType = gtsmodel.ActivityStreamsObject(statusable.GetTypeName())
status.ActivityStreamsType = statusable.GetTypeName()
return status, nil
}
@ -341,6 +344,40 @@ func (c *converter) ASFollowToFollowRequest(followable Followable) (*gtsmodel.Fo
return followRequest, nil
}
func (c *converter) ASFollowToFollow(followable Followable) (*gtsmodel.Follow, error) {
idProp := followable.GetJSONLDId()
if idProp == nil || !idProp.IsIRI() {
return nil, errors.New("no id property set on follow, or was not an iri")
}
uri := idProp.GetIRI().String()
origin, err := extractActor(followable)
if err != nil {
return nil, errors.New("error extracting actor property from follow")
}
originAccount := &gtsmodel.Account{}
if err := c.db.GetWhere("uri", origin.String(), originAccount); err != nil {
return nil, fmt.Errorf("error extracting account with uri %s from the database: %s", origin.String(), err)
}
target, err := extractObject(followable)
if err != nil {
return nil, errors.New("error extracting object property from follow")
}
targetAccount := &gtsmodel.Account{}
if err := c.db.GetWhere("uri", target.String(), targetAccount); err != nil {
return nil, fmt.Errorf("error extracting account with uri %s from the database: %s", origin.String(), err)
}
follow := &gtsmodel.Follow{
URI: uri,
AccountID: originAccount.ID,
TargetAccountID: targetAccount.ID,
}
return follow, nil
}
func isPublic(tos []*url.URL) bool {
for _, entry := range tos {
if strings.EqualFold(entry.String(), "https://www.w3.org/ns/activitystreams#Public") {

View File

@ -97,7 +97,9 @@ type TypeConverter interface {
ASStatusToStatus(statusable Statusable) (*gtsmodel.Status, error)
// ASFollowToFollowRequest converts a remote activitystreams `follow` representation into gts model follow request.
ASFollowToFollowRequest(followable Followable) (*gtsmodel.FollowRequest, error)
// ASFollowToFollowRequest converts a remote activitystreams `follow` representation into gts model follow.
ASFollowToFollow(followable Followable) (*gtsmodel.Follow, error)
/*
INTERNAL (gts) MODEL TO ACTIVITYSTREAMS MODEL
*/

View File

@ -48,6 +48,7 @@ import (
var Run action.GTSAction = func(ctx context.Context, _ *config.Config, log *logrus.Logger) error {
c := NewTestConfig()
dbService := NewTestDB()
federatingDB := NewTestFederatingDB(dbService)
router := NewTestRouter()
storageBackend := NewTestStorage()
@ -59,7 +60,7 @@ var Run action.GTSAction = func(ctx context.Context, _ *config.Config, log *logr
Body: r,
}, nil
}))
federator := federation.NewFederator(dbService, transportController, c, log, typeConverter)
federator := federation.NewFederator(dbService, federatingDB, transportController, c, log, typeConverter)
processor := NewTestProcessor(dbService, storageBackend, federator)
if err := processor.Start(); err != nil {
return fmt.Errorf("error starting processor: %s", err)

11
testrig/federatingdb.go Normal file
View File

@ -0,0 +1,11 @@
package testrig
import (
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/federation"
)
// NewTestFederatingDB returns a federating DB with the underlying db
func NewTestFederatingDB(db db.DB) federation.FederatingDB {
return federation.NewFederatingDB(db, NewTestConfig(), NewTestLog())
}

View File

@ -26,5 +26,5 @@ import (
// NewTestFederator returns a federator with the given database and (mock!!) transport controller.
func NewTestFederator(db db.DB, tc transport.Controller) federation.Federator {
return federation.NewFederator(db, tc, NewTestConfig(), NewTestLog(), NewTestTypeConverter(db))
return federation.NewFederator(db, NewTestFederatingDB(db), tc, NewTestConfig(), NewTestLog(), NewTestTypeConverter(db))
}