some tidying, some favedby, the usual
This commit is contained in:
parent
ddfb9aae65
commit
c92a72fdea
|
@ -74,9 +74,9 @@
|
|||
* [x] /api/v1/statuses/:id DELETE (Delete a status)
|
||||
* [ ] /api/v1/statuses/:id/context GET (View statuses above and below status ID)
|
||||
* [ ] /api/v1/statuses/:id/reblogged_by GET (See who has reblogged a status)
|
||||
* [ ] /api/v1/statuses/:id/favourited_by GET (See who has faved a status)
|
||||
* [ ] /api/v1/statuses/:id/favourite POST (Fave a status)
|
||||
* [ ] /api/v1/statuses/:id/favourite POST (Unfave a status)
|
||||
* [x] /api/v1/statuses/:id/favourited_by GET (See who has faved a status)
|
||||
* [x] /api/v1/statuses/:id/favourite POST (Fave a status)
|
||||
* [x] /api/v1/statuses/:id/unfavourite POST (Unfave a status)
|
||||
* [ ] /api/v1/statuses/:id/reblog POST (Reblog a status)
|
||||
* [ ] /api/v1/statuses/:id/unreblog POST (Undo a reblog)
|
||||
* [ ] /api/v1/statuses/:id/bookmark POST (Bookmark a status)
|
||||
|
|
|
@ -69,6 +69,7 @@ func New(config *config.Config, db db.DB, oauthServer oauth.Server, mediaHandler
|
|||
func (m *accountModule) Route(r router.Router) error {
|
||||
r.AttachHandler(http.MethodPost, basePath, m.accountCreatePOSTHandler)
|
||||
r.AttachHandler(http.MethodGet, basePathWithID, m.muxHandler)
|
||||
r.AttachHandler(http.MethodPatch, basePathWithID, m.muxHandler)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -94,11 +95,16 @@ func (m *accountModule) CreateTables(db db.DB) error {
|
|||
|
||||
func (m *accountModule) muxHandler(c *gin.Context) {
|
||||
ru := c.Request.RequestURI
|
||||
if strings.HasPrefix(ru, verifyPath) {
|
||||
m.accountVerifyGETHandler(c)
|
||||
} else if strings.HasPrefix(ru, updateCredentialsPath) {
|
||||
m.accountUpdateCredentialsPATCHHandler(c)
|
||||
} else {
|
||||
m.accountGETHandler(c)
|
||||
switch c.Request.Method {
|
||||
case http.MethodGet:
|
||||
if strings.HasPrefix(ru, verifyPath) {
|
||||
m.accountVerifyGETHandler(c)
|
||||
} else {
|
||||
m.accountGETHandler(c)
|
||||
}
|
||||
case http.MethodPatch:
|
||||
if strings.HasPrefix(ru, updateCredentialsPath) {
|
||||
m.accountUpdateCredentialsPATCHHandler(c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -156,6 +156,8 @@ func (m *FileServer) serveAttachment(c *gin.Context, accountID string, mediaType
|
|||
return
|
||||
}
|
||||
|
||||
l.Errorf("about to serve content length: %d attachment bytes is: %d", int64(contentLength), int64(len(attachmentBytes)))
|
||||
|
||||
// finally we can return with all the information we derived above
|
||||
c.DataFromReader(http.StatusOK, int64(contentLength), contentType, bytes.NewReader(attachmentBytes), map[string]string{})
|
||||
}
|
||||
|
|
|
@ -36,21 +36,28 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
IDKey = "id"
|
||||
BasePath = "/api/v1/statuses"
|
||||
BasePathWithID = BasePath + "/:" + IDKey
|
||||
ContextPath = BasePath + "/context"
|
||||
RebloggedPath = BasePath + "/reblogged_by"
|
||||
FavouritedPath = BasePath + "/favourited_by"
|
||||
FavouritePath = BasePath + "/favourite"
|
||||
ReblogPath = BasePath + "/reblog"
|
||||
UnreblogPath = BasePath + "/unreblog"
|
||||
BookmarkPath = BasePath + "/bookmark"
|
||||
UnbookmarkPath = BasePath + "/unbookmark"
|
||||
MutePath = BasePath + "/mute"
|
||||
UnmutePath = BasePath + "/unmute"
|
||||
PinPath = BasePath + "/pin"
|
||||
UnpinPath = BasePath + "/unpin"
|
||||
IDKey = "id"
|
||||
BasePath = "/api/v1/statuses"
|
||||
BasePathWithID = BasePath + "/:" + IDKey
|
||||
|
||||
ContextPath = BasePathWithID + "/context"
|
||||
|
||||
FavouritedPath = BasePathWithID + "/favourited_by"
|
||||
FavouritePath = BasePathWithID + "/favourite"
|
||||
UnfavouritePath = BasePathWithID + "/unfavourite"
|
||||
|
||||
RebloggedPath = BasePathWithID + "/reblogged_by"
|
||||
ReblogPath = BasePathWithID + "/reblog"
|
||||
UnreblogPath = BasePathWithID + "/unreblog"
|
||||
|
||||
BookmarkPath = BasePathWithID + "/bookmark"
|
||||
UnbookmarkPath = BasePathWithID + "/unbookmark"
|
||||
|
||||
MutePath = BasePathWithID + "/mute"
|
||||
UnmutePath = BasePathWithID + "/unmute"
|
||||
|
||||
PinPath = BasePathWithID + "/pin"
|
||||
UnpinPath = BasePathWithID + "/unpin"
|
||||
)
|
||||
|
||||
type StatusModule struct {
|
||||
|
@ -77,8 +84,13 @@ func New(config *config.Config, db db.DB, mediaHandler media.MediaHandler, masto
|
|||
// Route attaches all routes from this module to the given router
|
||||
func (m *StatusModule) Route(r router.Router) error {
|
||||
r.AttachHandler(http.MethodPost, BasePath, m.StatusCreatePOSTHandler)
|
||||
r.AttachHandler(http.MethodDelete, BasePathWithID, m.StatusDELETEHandler)
|
||||
|
||||
r.AttachHandler(http.MethodPost, FavouritePath, m.StatusFavePOSTHandler)
|
||||
r.AttachHandler(http.MethodPost, UnfavouritePath, m.StatusFavePOSTHandler)
|
||||
|
||||
|
||||
r.AttachHandler(http.MethodGet, BasePathWithID, m.muxHandler)
|
||||
r.AttachHandler(http.MethodDelete, BasePathWithID, m.muxHandler)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -113,16 +125,15 @@ func (m *StatusModule) CreateTables(db db.DB) error {
|
|||
func (m *StatusModule) muxHandler(c *gin.Context) {
|
||||
m.log.Debug("entering mux handler")
|
||||
ru := c.Request.RequestURI
|
||||
if c.Request.Method == http.MethodGet {
|
||||
|
||||
switch c.Request.Method {
|
||||
case http.MethodGet:
|
||||
if strings.HasPrefix(ru, ContextPath) {
|
||||
// TODO
|
||||
} else if strings.HasPrefix(ru, RebloggedPath) {
|
||||
// TODO
|
||||
} else if strings.HasPrefix(ru, FavouritedPath) {
|
||||
m.StatusFavedByGETHandler(c)
|
||||
} else {
|
||||
m.StatusGETHandler(c)
|
||||
}
|
||||
}
|
||||
if c.Request.Method == http.MethodDelete {
|
||||
m.StatusDELETEHandler(c)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,136 @@
|
|||
/*
|
||||
GoToSocial
|
||||
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package status
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/distributor"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
||||
func (m *StatusModule) StatusFavePOSTHandler(c *gin.Context) {
|
||||
l := m.log.WithFields(logrus.Fields{
|
||||
"func": "StatusFavePOSTHandler",
|
||||
"request_uri": c.Request.RequestURI,
|
||||
"user_agent": c.Request.UserAgent(),
|
||||
"origin_ip": c.ClientIP(),
|
||||
})
|
||||
l.Debugf("entering function")
|
||||
|
||||
authed, err := oauth.MustAuth(c, true, false, true, true) // we don't really need an app here but we want everything else
|
||||
if err != nil {
|
||||
l.Debug("not authed so can't fave status")
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "not authorized"})
|
||||
return
|
||||
}
|
||||
|
||||
targetStatusID := c.Param(IDKey)
|
||||
if targetStatusID == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "no status id provided"})
|
||||
return
|
||||
}
|
||||
|
||||
l.Tracef("going to search for target status %s", targetStatusID)
|
||||
targetStatus := >smodel.Status{}
|
||||
if err := m.db.GetByID(targetStatusID, targetStatus); err != nil {
|
||||
l.Errorf("error fetching status %s: %s", targetStatusID, err)
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": fmt.Sprintf("status %s not found", targetStatusID)})
|
||||
return
|
||||
}
|
||||
|
||||
l.Tracef("going to search for target account %s", targetStatus.AccountID)
|
||||
targetAccount := >smodel.Account{}
|
||||
if err := m.db.GetByID(targetStatus.AccountID, targetAccount); err != nil {
|
||||
l.Errorf("error fetching target account %s: %s", targetStatus.AccountID, err)
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": fmt.Sprintf("status %s not found", targetStatusID)})
|
||||
return
|
||||
}
|
||||
|
||||
l.Trace("going to get relevant accounts")
|
||||
relevantAccounts, err := m.db.PullRelevantAccountsFromStatus(targetStatus)
|
||||
if err != nil {
|
||||
l.Errorf("error fetching related accounts for status %s: %s", targetStatusID, err)
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": fmt.Sprintf("status %s not found", targetStatusID)})
|
||||
return
|
||||
}
|
||||
|
||||
l.Trace("going to see if status is visible")
|
||||
visible, err := m.db.StatusVisible(targetStatus, targetAccount, authed.Account, relevantAccounts) // requestingAccount might well be nil here, but StatusVisible knows how to take care of that
|
||||
if err != nil {
|
||||
l.Errorf("error seeing if status %s is visible: %s", targetStatus.ID, err)
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": fmt.Sprintf("status %s not found", targetStatusID)})
|
||||
return
|
||||
}
|
||||
|
||||
if !visible {
|
||||
l.Trace("status is not visible")
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": fmt.Sprintf("status %s not found", targetStatusID)})
|
||||
return
|
||||
}
|
||||
|
||||
// is the status faveable?
|
||||
if !targetStatus.VisibilityAdvanced.Likeable {
|
||||
l.Debug("status is not faveable")
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": fmt.Sprintf("status %s not faveable", targetStatusID)})
|
||||
return
|
||||
}
|
||||
|
||||
// it's visible! it's faveable! so let's fave the FUCK out of it
|
||||
fave, err := m.db.FaveStatus(targetStatus, authed.Account.ID)
|
||||
if err != nil {
|
||||
l.Debugf("error faveing status: %s", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
var boostOfStatus *gtsmodel.Status
|
||||
if targetStatus.BoostOfID != "" {
|
||||
boostOfStatus = >smodel.Status{}
|
||||
if err := m.db.GetByID(targetStatus.BoostOfID, boostOfStatus); err != nil {
|
||||
l.Errorf("error fetching boosted status %s: %s", targetStatus.BoostOfID, err)
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": fmt.Sprintf("status %s not found", targetStatusID)})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
mastoStatus, err := m.mastoConverter.StatusToMasto(targetStatus, targetAccount, authed.Account, relevantAccounts.BoostedAccount, relevantAccounts.ReplyToAccount, boostOfStatus)
|
||||
if err != nil {
|
||||
l.Errorf("error converting status %s to frontend representation: %s", targetStatus.ID, err)
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": fmt.Sprintf("status %s not found", targetStatusID)})
|
||||
return
|
||||
}
|
||||
|
||||
// if the targeted status was already faved, faved will be nil
|
||||
// only put the fave in the distributor if something actually changed
|
||||
if fave != nil {
|
||||
fave.FavedStatus = targetStatus // attach the status pointer to the fave for easy retrieval in the distributor
|
||||
m.distributor.FromClientAPI() <- distributor.FromClientAPI{
|
||||
APObjectType: gtsmodel.ActivityStreamsNote, // status is a note
|
||||
APActivityType: gtsmodel.ActivityStreamsLike, // we're creating a like/fave on the note
|
||||
Activity: fave, // pass the fave along for processing
|
||||
}
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, mastoStatus)
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
/*
|
||||
GoToSocial
|
||||
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package status
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db/gtsmodel"
|
||||
mastotypes "github.com/superseriousbusiness/gotosocial/internal/mastotypes/mastomodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
||||
func (m *StatusModule) StatusFavedByGETHandler(c *gin.Context) {
|
||||
l := m.log.WithFields(logrus.Fields{
|
||||
"func": "statusGETHandler",
|
||||
"request_uri": c.Request.RequestURI,
|
||||
"user_agent": c.Request.UserAgent(),
|
||||
"origin_ip": c.ClientIP(),
|
||||
})
|
||||
l.Debugf("entering function")
|
||||
|
||||
var requestingAccount *gtsmodel.Account
|
||||
authed, err := oauth.MustAuth(c, true, false, true, true) // we don't really need an app here but we want everything else
|
||||
if err != nil {
|
||||
l.Debug("not authed but will continue to serve anyway if public status")
|
||||
requestingAccount = nil
|
||||
} else {
|
||||
requestingAccount = authed.Account
|
||||
}
|
||||
|
||||
targetStatusID := c.Param(IDKey)
|
||||
if targetStatusID == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "no status id provided"})
|
||||
return
|
||||
}
|
||||
|
||||
l.Tracef("going to search for target status %s", targetStatusID)
|
||||
targetStatus := >smodel.Status{}
|
||||
if err := m.db.GetByID(targetStatusID, targetStatus); err != nil {
|
||||
l.Errorf("error fetching status %s: %s", targetStatusID, err)
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": fmt.Sprintf("status %s not found", targetStatusID)})
|
||||
return
|
||||
}
|
||||
|
||||
l.Tracef("going to search for target account %s", targetStatus.AccountID)
|
||||
targetAccount := >smodel.Account{}
|
||||
if err := m.db.GetByID(targetStatus.AccountID, targetAccount); err != nil {
|
||||
l.Errorf("error fetching target account %s: %s", targetStatus.AccountID, err)
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": fmt.Sprintf("status %s not found", targetStatusID)})
|
||||
return
|
||||
}
|
||||
|
||||
l.Trace("going to get relevant accounts")
|
||||
relevantAccounts, err := m.db.PullRelevantAccountsFromStatus(targetStatus)
|
||||
if err != nil {
|
||||
l.Errorf("error fetching related accounts for status %s: %s", targetStatusID, err)
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": fmt.Sprintf("status %s not found", targetStatusID)})
|
||||
return
|
||||
}
|
||||
|
||||
l.Trace("going to see if status is visible")
|
||||
visible, err := m.db.StatusVisible(targetStatus, targetAccount, requestingAccount, relevantAccounts) // requestingAccount might well be nil here, but StatusVisible knows how to take care of that
|
||||
if err != nil {
|
||||
l.Errorf("error seeing if status %s is visible: %s", targetStatus.ID, err)
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": fmt.Sprintf("status %s not found", targetStatusID)})
|
||||
return
|
||||
}
|
||||
|
||||
if !visible {
|
||||
l.Trace("status is not visible")
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": fmt.Sprintf("status %s not found", targetStatusID)})
|
||||
return
|
||||
}
|
||||
|
||||
// get ALL accounts that faved a status -- doesn't take account of blocks and mutes and stuff
|
||||
favingAccounts, err := m.db.WhoFavedStatus(targetStatus)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error":err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// filter the list so the user doesn't see accounts they blocked or which blocked them
|
||||
filteredAccounts := []*gtsmodel.Account{}
|
||||
for _, acc := range favingAccounts {
|
||||
blocked, err := m.db.Blocked(authed.Account.ID, acc.ID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error":err.Error()})
|
||||
return
|
||||
}
|
||||
if !blocked {
|
||||
filteredAccounts = append(filteredAccounts, acc)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: filter other things here? suspended? muted? silenced?
|
||||
|
||||
// now we can return the masto representation of those accounts
|
||||
mastoAccounts := []*mastotypes.Account{}
|
||||
for _, acc := range filteredAccounts {
|
||||
mastoAccount, err := m.mastoConverter.AccountToMastoPublic(acc)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error":err.Error()})
|
||||
return
|
||||
}
|
||||
mastoAccounts = append(mastoAccounts, mastoAccount)
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, mastoAccounts)
|
||||
}
|
|
@ -0,0 +1,136 @@
|
|||
/*
|
||||
GoToSocial
|
||||
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package status
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/distributor"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
||||
func (m *StatusModule) StatusUnfavePOSTHandler(c *gin.Context) {
|
||||
l := m.log.WithFields(logrus.Fields{
|
||||
"func": "StatusUnfavePOSTHandler",
|
||||
"request_uri": c.Request.RequestURI,
|
||||
"user_agent": c.Request.UserAgent(),
|
||||
"origin_ip": c.ClientIP(),
|
||||
})
|
||||
l.Debugf("entering function")
|
||||
|
||||
authed, err := oauth.MustAuth(c, true, false, true, true) // we don't really need an app here but we want everything else
|
||||
if err != nil {
|
||||
l.Debug("not authed so can't unfave status")
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "not authorized"})
|
||||
return
|
||||
}
|
||||
|
||||
targetStatusID := c.Param(IDKey)
|
||||
if targetStatusID == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "no status id provided"})
|
||||
return
|
||||
}
|
||||
|
||||
l.Tracef("going to search for target status %s", targetStatusID)
|
||||
targetStatus := >smodel.Status{}
|
||||
if err := m.db.GetByID(targetStatusID, targetStatus); err != nil {
|
||||
l.Errorf("error fetching status %s: %s", targetStatusID, err)
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": fmt.Sprintf("status %s not found", targetStatusID)})
|
||||
return
|
||||
}
|
||||
|
||||
l.Tracef("going to search for target account %s", targetStatus.AccountID)
|
||||
targetAccount := >smodel.Account{}
|
||||
if err := m.db.GetByID(targetStatus.AccountID, targetAccount); err != nil {
|
||||
l.Errorf("error fetching target account %s: %s", targetStatus.AccountID, err)
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": fmt.Sprintf("status %s not found", targetStatusID)})
|
||||
return
|
||||
}
|
||||
|
||||
l.Trace("going to get relevant accounts")
|
||||
relevantAccounts, err := m.db.PullRelevantAccountsFromStatus(targetStatus)
|
||||
if err != nil {
|
||||
l.Errorf("error fetching related accounts for status %s: %s", targetStatusID, err)
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": fmt.Sprintf("status %s not found", targetStatusID)})
|
||||
return
|
||||
}
|
||||
|
||||
l.Trace("going to see if status is visible")
|
||||
visible, err := m.db.StatusVisible(targetStatus, targetAccount, authed.Account, relevantAccounts) // requestingAccount might well be nil here, but StatusVisible knows how to take care of that
|
||||
if err != nil {
|
||||
l.Errorf("error seeing if status %s is visible: %s", targetStatus.ID, err)
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": fmt.Sprintf("status %s not found", targetStatusID)})
|
||||
return
|
||||
}
|
||||
|
||||
if !visible {
|
||||
l.Trace("status is not visible")
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": fmt.Sprintf("status %s not found", targetStatusID)})
|
||||
return
|
||||
}
|
||||
|
||||
// is the status faveable?
|
||||
if !targetStatus.VisibilityAdvanced.Likeable {
|
||||
l.Debug("status is not faveable")
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": fmt.Sprintf("status %s not faveable so therefore not unfave-able", targetStatusID)})
|
||||
return
|
||||
}
|
||||
|
||||
// it's visible! it's faveable! so let's unfave the FUCK out of it
|
||||
fave, err := m.db.UnfaveStatus(targetStatus, authed.Account.ID)
|
||||
if err != nil {
|
||||
l.Debugf("error unfaveing status: %s", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
var boostOfStatus *gtsmodel.Status
|
||||
if targetStatus.BoostOfID != "" {
|
||||
boostOfStatus = >smodel.Status{}
|
||||
if err := m.db.GetByID(targetStatus.BoostOfID, boostOfStatus); err != nil {
|
||||
l.Errorf("error fetching boosted status %s: %s", targetStatus.BoostOfID, err)
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": fmt.Sprintf("status %s not found", targetStatusID)})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
mastoStatus, err := m.mastoConverter.StatusToMasto(targetStatus, targetAccount, authed.Account, relevantAccounts.BoostedAccount, relevantAccounts.ReplyToAccount, boostOfStatus)
|
||||
if err != nil {
|
||||
l.Errorf("error converting status %s to frontend representation: %s", targetStatus.ID, err)
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": fmt.Sprintf("status %s not found", targetStatusID)})
|
||||
return
|
||||
}
|
||||
|
||||
// fave might be nil if this status wasn't faved in the first place
|
||||
// we only want to pass the message to the distributor if something actually changed
|
||||
if fave != nil {
|
||||
fave.FavedStatus = targetStatus // attach the status pointer to the fave for easy retrieval in the distributor
|
||||
m.distributor.FromClientAPI() <- distributor.FromClientAPI{
|
||||
APObjectType: gtsmodel.ActivityStreamsNote, // status is a note
|
||||
APActivityType: gtsmodel.ActivityStreamsUndo, // undo the fave
|
||||
Activity: fave, // pass the undone fave along
|
||||
}
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, mastoStatus)
|
||||
}
|
|
@ -0,0 +1,208 @@
|
|||
/*
|
||||
GoToSocial
|
||||
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package status
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/apimodule/status"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/distributor"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/mastotypes"
|
||||
mastomodel "github.com/superseriousbusiness/gotosocial/internal/mastotypes/mastomodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/media"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/storage"
|
||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||
)
|
||||
|
||||
type StatusFaveTestSuite struct {
|
||||
// standard suite interfaces
|
||||
suite.Suite
|
||||
config *config.Config
|
||||
db db.DB
|
||||
log *logrus.Logger
|
||||
storage storage.Storage
|
||||
mastoConverter mastotypes.Converter
|
||||
mediaHandler media.MediaHandler
|
||||
oauthServer oauth.Server
|
||||
distributor distributor.Distributor
|
||||
|
||||
// standard suite models
|
||||
testTokens map[string]*oauth.Token
|
||||
testClients map[string]*oauth.Client
|
||||
testApplications map[string]*gtsmodel.Application
|
||||
testUsers map[string]*gtsmodel.User
|
||||
testAccounts map[string]*gtsmodel.Account
|
||||
testAttachments map[string]*gtsmodel.MediaAttachment
|
||||
testStatuses map[string]*gtsmodel.Status
|
||||
|
||||
// module being tested
|
||||
statusModule *status.StatusModule
|
||||
}
|
||||
|
||||
/*
|
||||
TEST INFRASTRUCTURE
|
||||
*/
|
||||
|
||||
// SetupSuite sets some variables on the suite that we can use as consts (more or less) throughout
|
||||
func (suite *StatusFaveTestSuite) SetupSuite() {
|
||||
// setup standard items
|
||||
suite.config = testrig.NewTestConfig()
|
||||
suite.db = testrig.NewTestDB()
|
||||
suite.log = testrig.NewTestLog()
|
||||
suite.storage = testrig.NewTestStorage()
|
||||
suite.mastoConverter = testrig.NewTestMastoConverter(suite.db)
|
||||
suite.mediaHandler = testrig.NewTestMediaHandler(suite.db, suite.storage)
|
||||
suite.oauthServer = testrig.NewTestOauthServer(suite.db)
|
||||
suite.distributor = testrig.NewTestDistributor()
|
||||
|
||||
// setup module being tested
|
||||
suite.statusModule = status.New(suite.config, suite.db, suite.mediaHandler, suite.mastoConverter, suite.distributor, suite.log).(*status.StatusModule)
|
||||
}
|
||||
|
||||
func (suite *StatusFaveTestSuite) TearDownSuite() {
|
||||
testrig.StandardDBTeardown(suite.db)
|
||||
testrig.StandardStorageTeardown(suite.storage)
|
||||
}
|
||||
|
||||
func (suite *StatusFaveTestSuite) SetupTest() {
|
||||
testrig.StandardDBSetup(suite.db)
|
||||
testrig.StandardStorageSetup(suite.storage, "../../../../testrig/media")
|
||||
suite.testTokens = testrig.NewTestTokens()
|
||||
suite.testClients = testrig.NewTestClients()
|
||||
suite.testApplications = testrig.NewTestApplications()
|
||||
suite.testUsers = testrig.NewTestUsers()
|
||||
suite.testAccounts = testrig.NewTestAccounts()
|
||||
suite.testAttachments = testrig.NewTestAttachments()
|
||||
suite.testStatuses = testrig.NewTestStatuses()
|
||||
}
|
||||
|
||||
// TearDownTest drops tables to make sure there's no data in the db
|
||||
func (suite *StatusFaveTestSuite) TearDownTest() {
|
||||
testrig.StandardDBTeardown(suite.db)
|
||||
testrig.StandardStorageTeardown(suite.storage)
|
||||
}
|
||||
|
||||
/*
|
||||
ACTUAL TESTS
|
||||
*/
|
||||
|
||||
// fave a status
|
||||
func (suite *StatusFaveTestSuite) TestPostFave() {
|
||||
|
||||
t := suite.testTokens["local_account_1"]
|
||||
oauthToken := oauth.PGTokenToOauthToken(t)
|
||||
|
||||
|
||||
targetStatus := suite.testStatuses["admin_account_status_2"]
|
||||
|
||||
// setup
|
||||
recorder := httptest.NewRecorder()
|
||||
ctx, _ := gin.CreateTestContext(recorder)
|
||||
ctx.Set(oauth.SessionAuthorizedApplication, suite.testApplications["application_1"])
|
||||
ctx.Set(oauth.SessionAuthorizedToken, oauthToken)
|
||||
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"])
|
||||
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"])
|
||||
ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080%s", strings.Replace(status.FavouritePath, ":id", targetStatus.ID, 1)), nil) // the endpoint we're hitting
|
||||
|
||||
// normally the router would populate these params from the path values,
|
||||
// but because we're calling the function directly, we need to set them manually.
|
||||
ctx.Params = gin.Params{
|
||||
gin.Param{
|
||||
Key: status.IDKey,
|
||||
Value: targetStatus.ID,
|
||||
},
|
||||
}
|
||||
|
||||
suite.statusModule.StatusFavePOSTHandler(ctx)
|
||||
|
||||
// check response
|
||||
suite.EqualValues(http.StatusOK, recorder.Code)
|
||||
|
||||
result := recorder.Result()
|
||||
defer result.Body.Close()
|
||||
b, err := ioutil.ReadAll(result.Body)
|
||||
assert.NoError(suite.T(), err)
|
||||
|
||||
statusReply := &mastomodel.Status{}
|
||||
err = json.Unmarshal(b, statusReply)
|
||||
assert.NoError(suite.T(), err)
|
||||
|
||||
assert.Equal(suite.T(), targetStatus.ContentWarning, statusReply.SpoilerText)
|
||||
assert.Equal(suite.T(), targetStatus.Content, statusReply.Content)
|
||||
assert.True(suite.T(), statusReply.Sensitive)
|
||||
assert.Equal(suite.T(), mastomodel.VisibilityPublic, statusReply.Visibility)
|
||||
assert.True(suite.T(), statusReply.Favourited)
|
||||
assert.Equal(suite.T(), 1, statusReply.FavouritesCount)
|
||||
}
|
||||
|
||||
// try to fave a status that's not faveable
|
||||
func (suite *StatusFaveTestSuite) TestPostUnfaveable() {
|
||||
|
||||
t := suite.testTokens["local_account_1"]
|
||||
oauthToken := oauth.PGTokenToOauthToken(t)
|
||||
|
||||
targetStatus := suite.testStatuses["local_account_2_status_3"] // this one is unlikeable and unreplyable
|
||||
|
||||
// setup
|
||||
recorder := httptest.NewRecorder()
|
||||
ctx, _ := gin.CreateTestContext(recorder)
|
||||
ctx.Set(oauth.SessionAuthorizedApplication, suite.testApplications["application_1"])
|
||||
ctx.Set(oauth.SessionAuthorizedToken, oauthToken)
|
||||
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"])
|
||||
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"])
|
||||
ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080%s", strings.Replace(status.FavouritePath, ":id", targetStatus.ID, 1)), nil) // the endpoint we're hitting
|
||||
|
||||
// normally the router would populate these params from the path values,
|
||||
// but because we're calling the function directly, we need to set them manually.
|
||||
ctx.Params = gin.Params{
|
||||
gin.Param{
|
||||
Key: status.IDKey,
|
||||
Value: targetStatus.ID,
|
||||
},
|
||||
}
|
||||
|
||||
suite.statusModule.StatusFavePOSTHandler(ctx)
|
||||
|
||||
// check response
|
||||
suite.EqualValues(http.StatusForbidden, recorder.Code) // we 403 unlikeable statuses
|
||||
|
||||
result := recorder.Result()
|
||||
defer result.Body.Close()
|
||||
b, err := ioutil.ReadAll(result.Body)
|
||||
assert.NoError(suite.T(), err)
|
||||
assert.Equal(suite.T(), fmt.Sprintf(`{"error":"status %s not faveable"}`, targetStatus.ID), string(b))
|
||||
}
|
||||
|
||||
func TestStatusFaveTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(StatusFaveTestSuite))
|
||||
}
|
|
@ -0,0 +1,159 @@
|
|||
/*
|
||||
GoToSocial
|
||||
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package status
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/apimodule/status"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/distributor"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/mastotypes"
|
||||
mastomodel "github.com/superseriousbusiness/gotosocial/internal/mastotypes/mastomodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/media"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/storage"
|
||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||
)
|
||||
|
||||
type StatusFavedByTestSuite struct {
|
||||
// standard suite interfaces
|
||||
suite.Suite
|
||||
config *config.Config
|
||||
db db.DB
|
||||
log *logrus.Logger
|
||||
storage storage.Storage
|
||||
mastoConverter mastotypes.Converter
|
||||
mediaHandler media.MediaHandler
|
||||
oauthServer oauth.Server
|
||||
distributor distributor.Distributor
|
||||
|
||||
// standard suite models
|
||||
testTokens map[string]*oauth.Token
|
||||
testClients map[string]*oauth.Client
|
||||
testApplications map[string]*gtsmodel.Application
|
||||
testUsers map[string]*gtsmodel.User
|
||||
testAccounts map[string]*gtsmodel.Account
|
||||
testAttachments map[string]*gtsmodel.MediaAttachment
|
||||
testStatuses map[string]*gtsmodel.Status
|
||||
|
||||
// module being tested
|
||||
statusModule *status.StatusModule
|
||||
}
|
||||
|
||||
// SetupSuite sets some variables on the suite that we can use as consts (more or less) throughout
|
||||
func (suite *StatusFavedByTestSuite) SetupSuite() {
|
||||
// setup standard items
|
||||
suite.config = testrig.NewTestConfig()
|
||||
suite.db = testrig.NewTestDB()
|
||||
suite.log = testrig.NewTestLog()
|
||||
suite.storage = testrig.NewTestStorage()
|
||||
suite.mastoConverter = testrig.NewTestMastoConverter(suite.db)
|
||||
suite.mediaHandler = testrig.NewTestMediaHandler(suite.db, suite.storage)
|
||||
suite.oauthServer = testrig.NewTestOauthServer(suite.db)
|
||||
suite.distributor = testrig.NewTestDistributor()
|
||||
|
||||
// setup module being tested
|
||||
suite.statusModule = status.New(suite.config, suite.db, suite.mediaHandler, suite.mastoConverter, suite.distributor, suite.log).(*status.StatusModule)
|
||||
}
|
||||
|
||||
func (suite *StatusFavedByTestSuite) TearDownSuite() {
|
||||
testrig.StandardDBTeardown(suite.db)
|
||||
testrig.StandardStorageTeardown(suite.storage)
|
||||
}
|
||||
|
||||
func (suite *StatusFavedByTestSuite) SetupTest() {
|
||||
testrig.StandardDBSetup(suite.db)
|
||||
testrig.StandardStorageSetup(suite.storage, "../../../../testrig/media")
|
||||
suite.testTokens = testrig.NewTestTokens()
|
||||
suite.testClients = testrig.NewTestClients()
|
||||
suite.testApplications = testrig.NewTestApplications()
|
||||
suite.testUsers = testrig.NewTestUsers()
|
||||
suite.testAccounts = testrig.NewTestAccounts()
|
||||
suite.testAttachments = testrig.NewTestAttachments()
|
||||
suite.testStatuses = testrig.NewTestStatuses()
|
||||
}
|
||||
|
||||
// TearDownTest drops tables to make sure there's no data in the db
|
||||
func (suite *StatusFavedByTestSuite) TearDownTest() {
|
||||
testrig.StandardDBTeardown(suite.db)
|
||||
testrig.StandardStorageTeardown(suite.storage)
|
||||
}
|
||||
|
||||
/*
|
||||
ACTUAL TESTS
|
||||
*/
|
||||
|
||||
func (suite *StatusFavedByTestSuite) TestGetFavedBy() {
|
||||
t := suite.testTokens["local_account_2"]
|
||||
oauthToken := oauth.PGTokenToOauthToken(t)
|
||||
|
||||
targetStatus := suite.testStatuses["admin_account_status_1"] // this status is faved by local_account_1
|
||||
|
||||
// setup
|
||||
recorder := httptest.NewRecorder()
|
||||
ctx, _ := gin.CreateTestContext(recorder)
|
||||
ctx.Set(oauth.SessionAuthorizedApplication, suite.testApplications["application_2"])
|
||||
ctx.Set(oauth.SessionAuthorizedToken, oauthToken)
|
||||
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_2"])
|
||||
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_2"])
|
||||
ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080%s", strings.Replace(status.FavouritedPath, ":id", targetStatus.ID, 1)), nil) // the endpoint we're hitting
|
||||
|
||||
// normally the router would populate these params from the path values,
|
||||
// but because we're calling the function directly, we need to set them manually.
|
||||
ctx.Params = gin.Params{
|
||||
gin.Param{
|
||||
Key: status.IDKey,
|
||||
Value: targetStatus.ID,
|
||||
},
|
||||
}
|
||||
|
||||
suite.statusModule.StatusFavedByGETHandler(ctx)
|
||||
|
||||
// check response
|
||||
suite.EqualValues(http.StatusOK, recorder.Code)
|
||||
|
||||
result := recorder.Result()
|
||||
defer result.Body.Close()
|
||||
b, err := ioutil.ReadAll(result.Body)
|
||||
assert.NoError(suite.T(), err)
|
||||
|
||||
accts := []mastomodel.Account{}
|
||||
err = json.Unmarshal(b, &accts)
|
||||
assert.NoError(suite.T(), err)
|
||||
|
||||
assert.Len(suite.T(), accts, 1)
|
||||
assert.Equal(suite.T(), "the_mighty_zork", accts[0].Username)
|
||||
}
|
||||
|
||||
func TestStatusFavedByTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(StatusFavedByTestSuite))
|
||||
}
|
|
@ -0,0 +1,219 @@
|
|||
/*
|
||||
GoToSocial
|
||||
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package status
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/apimodule/status"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/distributor"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/mastotypes"
|
||||
mastomodel "github.com/superseriousbusiness/gotosocial/internal/mastotypes/mastomodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/media"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/storage"
|
||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||
)
|
||||
|
||||
type StatusUnfaveTestSuite struct {
|
||||
// standard suite interfaces
|
||||
suite.Suite
|
||||
config *config.Config
|
||||
db db.DB
|
||||
log *logrus.Logger
|
||||
storage storage.Storage
|
||||
mastoConverter mastotypes.Converter
|
||||
mediaHandler media.MediaHandler
|
||||
oauthServer oauth.Server
|
||||
distributor distributor.Distributor
|
||||
|
||||
// standard suite models
|
||||
testTokens map[string]*oauth.Token
|
||||
testClients map[string]*oauth.Client
|
||||
testApplications map[string]*gtsmodel.Application
|
||||
testUsers map[string]*gtsmodel.User
|
||||
testAccounts map[string]*gtsmodel.Account
|
||||
testAttachments map[string]*gtsmodel.MediaAttachment
|
||||
testStatuses map[string]*gtsmodel.Status
|
||||
|
||||
// module being tested
|
||||
statusModule *status.StatusModule
|
||||
}
|
||||
|
||||
/*
|
||||
TEST INFRASTRUCTURE
|
||||
*/
|
||||
|
||||
// SetupSuite sets some variables on the suite that we can use as consts (more or less) throughout
|
||||
func (suite *StatusUnfaveTestSuite) SetupSuite() {
|
||||
// setup standard items
|
||||
suite.config = testrig.NewTestConfig()
|
||||
suite.db = testrig.NewTestDB()
|
||||
suite.log = testrig.NewTestLog()
|
||||
suite.storage = testrig.NewTestStorage()
|
||||
suite.mastoConverter = testrig.NewTestMastoConverter(suite.db)
|
||||
suite.mediaHandler = testrig.NewTestMediaHandler(suite.db, suite.storage)
|
||||
suite.oauthServer = testrig.NewTestOauthServer(suite.db)
|
||||
suite.distributor = testrig.NewTestDistributor()
|
||||
|
||||
// setup module being tested
|
||||
suite.statusModule = status.New(suite.config, suite.db, suite.mediaHandler, suite.mastoConverter, suite.distributor, suite.log).(*status.StatusModule)
|
||||
}
|
||||
|
||||
func (suite *StatusUnfaveTestSuite) TearDownSuite() {
|
||||
testrig.StandardDBTeardown(suite.db)
|
||||
testrig.StandardStorageTeardown(suite.storage)
|
||||
}
|
||||
|
||||
func (suite *StatusUnfaveTestSuite) SetupTest() {
|
||||
testrig.StandardDBSetup(suite.db)
|
||||
testrig.StandardStorageSetup(suite.storage, "../../../../testrig/media")
|
||||
suite.testTokens = testrig.NewTestTokens()
|
||||
suite.testClients = testrig.NewTestClients()
|
||||
suite.testApplications = testrig.NewTestApplications()
|
||||
suite.testUsers = testrig.NewTestUsers()
|
||||
suite.testAccounts = testrig.NewTestAccounts()
|
||||
suite.testAttachments = testrig.NewTestAttachments()
|
||||
suite.testStatuses = testrig.NewTestStatuses()
|
||||
}
|
||||
|
||||
// TearDownTest drops tables to make sure there's no data in the db
|
||||
func (suite *StatusUnfaveTestSuite) TearDownTest() {
|
||||
testrig.StandardDBTeardown(suite.db)
|
||||
testrig.StandardStorageTeardown(suite.storage)
|
||||
}
|
||||
|
||||
/*
|
||||
ACTUAL TESTS
|
||||
*/
|
||||
|
||||
// unfave a status
|
||||
func (suite *StatusUnfaveTestSuite) TestPostUnfave() {
|
||||
|
||||
t := suite.testTokens["local_account_1"]
|
||||
oauthToken := oauth.PGTokenToOauthToken(t)
|
||||
|
||||
// this is the status we wanna unfave: in the testrig it's already faved by this account
|
||||
targetStatus := suite.testStatuses["admin_account_status_1"]
|
||||
|
||||
// setup
|
||||
recorder := httptest.NewRecorder()
|
||||
ctx, _ := gin.CreateTestContext(recorder)
|
||||
ctx.Set(oauth.SessionAuthorizedApplication, suite.testApplications["application_1"])
|
||||
ctx.Set(oauth.SessionAuthorizedToken, oauthToken)
|
||||
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"])
|
||||
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"])
|
||||
ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080%s", strings.Replace(status.UnfavouritePath, ":id", targetStatus.ID, 1)), nil) // the endpoint we're hitting
|
||||
|
||||
// normally the router would populate these params from the path values,
|
||||
// but because we're calling the function directly, we need to set them manually.
|
||||
ctx.Params = gin.Params{
|
||||
gin.Param{
|
||||
Key: status.IDKey,
|
||||
Value: targetStatus.ID,
|
||||
},
|
||||
}
|
||||
|
||||
suite.statusModule.StatusUnfavePOSTHandler(ctx)
|
||||
|
||||
// check response
|
||||
suite.EqualValues(http.StatusOK, recorder.Code)
|
||||
|
||||
result := recorder.Result()
|
||||
defer result.Body.Close()
|
||||
b, err := ioutil.ReadAll(result.Body)
|
||||
assert.NoError(suite.T(), err)
|
||||
|
||||
statusReply := &mastomodel.Status{}
|
||||
err = json.Unmarshal(b, statusReply)
|
||||
assert.NoError(suite.T(), err)
|
||||
|
||||
assert.Equal(suite.T(), targetStatus.ContentWarning, statusReply.SpoilerText)
|
||||
assert.Equal(suite.T(), targetStatus.Content, statusReply.Content)
|
||||
assert.False(suite.T(), statusReply.Sensitive)
|
||||
assert.Equal(suite.T(), mastomodel.VisibilityPublic, statusReply.Visibility)
|
||||
assert.False(suite.T(), statusReply.Favourited)
|
||||
assert.Equal(suite.T(), 0, statusReply.FavouritesCount)
|
||||
}
|
||||
|
||||
// try to unfave a status that's already not faved
|
||||
func (suite *StatusUnfaveTestSuite) TestPostAlreadyNotFaved() {
|
||||
|
||||
t := suite.testTokens["local_account_1"]
|
||||
oauthToken := oauth.PGTokenToOauthToken(t)
|
||||
|
||||
// this is the status we wanna unfave: in the testrig it's not faved by this account
|
||||
targetStatus := suite.testStatuses["admin_account_status_2"]
|
||||
|
||||
// setup
|
||||
recorder := httptest.NewRecorder()
|
||||
ctx, _ := gin.CreateTestContext(recorder)
|
||||
ctx.Set(oauth.SessionAuthorizedApplication, suite.testApplications["application_1"])
|
||||
ctx.Set(oauth.SessionAuthorizedToken, oauthToken)
|
||||
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"])
|
||||
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"])
|
||||
ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080%s", strings.Replace(status.UnfavouritePath, ":id", targetStatus.ID, 1)), nil) // the endpoint we're hitting
|
||||
|
||||
// normally the router would populate these params from the path values,
|
||||
// but because we're calling the function directly, we need to set them manually.
|
||||
ctx.Params = gin.Params{
|
||||
gin.Param{
|
||||
Key: status.IDKey,
|
||||
Value: targetStatus.ID,
|
||||
},
|
||||
}
|
||||
|
||||
suite.statusModule.StatusUnfavePOSTHandler(ctx)
|
||||
|
||||
// check response
|
||||
suite.EqualValues(http.StatusOK, recorder.Code)
|
||||
|
||||
result := recorder.Result()
|
||||
defer result.Body.Close()
|
||||
b, err := ioutil.ReadAll(result.Body)
|
||||
assert.NoError(suite.T(), err)
|
||||
|
||||
statusReply := &mastomodel.Status{}
|
||||
err = json.Unmarshal(b, statusReply)
|
||||
assert.NoError(suite.T(), err)
|
||||
|
||||
assert.Equal(suite.T(), targetStatus.ContentWarning, statusReply.SpoilerText)
|
||||
assert.Equal(suite.T(), targetStatus.Content, statusReply.Content)
|
||||
assert.True(suite.T(), statusReply.Sensitive)
|
||||
assert.Equal(suite.T(), mastomodel.VisibilityPublic, statusReply.Visibility)
|
||||
assert.False(suite.T(), statusReply.Favourited)
|
||||
assert.Equal(suite.T(), 0, statusReply.FavouritesCount)
|
||||
}
|
||||
|
||||
func TestStatusUnfaveTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(StatusUnfaveTestSuite))
|
||||
}
|
|
@ -235,6 +235,18 @@ type DB interface {
|
|||
// StatusPinnedBy checks if a given status has been pinned by a given account ID
|
||||
StatusPinnedBy(status *gtsmodel.Status, accountID string) (bool, error)
|
||||
|
||||
// FaveStatus faves the given status, using accountID as the faver.
|
||||
// The returned fave will be nil if the status was already faved.
|
||||
FaveStatus(status *gtsmodel.Status, accountID string) (*gtsmodel.StatusFave, error)
|
||||
|
||||
// UnfaveStatus unfaves the given status, using accountID as the unfaver (sure, that's a word).
|
||||
// The returned fave will be nil if the status was already not faved.
|
||||
UnfaveStatus(status *gtsmodel.Status, accountID string) (*gtsmodel.StatusFave, error)
|
||||
|
||||
// WhoFavedStatus returns a slice of accounts who faved the given status.
|
||||
// This slice will be unfiltered, not taking account of blocks and whatnot, so filter it before serving it back to a user.
|
||||
WhoFavedStatus(status *gtsmodel.Status) ([]*gtsmodel.Account, error)
|
||||
|
||||
/*
|
||||
USEFUL CONVERSION FUNCTIONS
|
||||
*/
|
||||
|
|
|
@ -44,26 +44,10 @@ type Account struct {
|
|||
ACCOUNT METADATA
|
||||
*/
|
||||
|
||||
// File name of the avatar on local storage
|
||||
AvatarFileName string
|
||||
// Gif? png? jpeg?
|
||||
AvatarContentType string
|
||||
// Size of the avatar in bytes
|
||||
AvatarFileSize int
|
||||
// When was the avatar last updated?
|
||||
AvatarUpdatedAt time.Time `pg:"type:timestamp"`
|
||||
// Where can the avatar be retrieved?
|
||||
AvatarRemoteURL string
|
||||
// File name of the header on local storage
|
||||
HeaderFileName string
|
||||
// Gif? png? jpeg?
|
||||
HeaderContentType string
|
||||
// Size of the header in bytes
|
||||
HeaderFileSize int
|
||||
// When was the header last updated?
|
||||
HeaderUpdatedAt time.Time `pg:"type:timestamp"`
|
||||
// Where can the header be retrieved?
|
||||
HeaderRemoteURL string
|
||||
// ID of the avatar as a media attachment
|
||||
AvatarMediaAttachmentID string
|
||||
// ID of the header as a media attachment
|
||||
HeaderMediaAttachmentID string
|
||||
// DisplayName for this account. Can be empty, then just the Username will be used for display purposes.
|
||||
DisplayName string
|
||||
// a key/value map of fields that this account has added to their profile
|
||||
|
|
|
@ -23,13 +23,16 @@ import "time"
|
|||
// StatusFave refers to a 'fave' or 'like' in the database, from one account, targeting the status of another account
|
||||
type StatusFave struct {
|
||||
// id of this fave in the database
|
||||
ID string `pg:"type:uuid,default:gen_random_uuid(),pk,notnull,unique"`
|
||||
ID string `pg:"type:uuid,default:gen_random_uuid(),pk,notnull,unique"`
|
||||
// when was this fave created
|
||||
CreatedAt time.Time `pg:"type:timestamp,notnull,default:now()"`
|
||||
CreatedAt time.Time `pg:"type:timestamp,notnull,default:now()"`
|
||||
// id of the account that created ('did') the fave
|
||||
AccountID string `pg:",notnull"`
|
||||
AccountID string `pg:",notnull"`
|
||||
// id the account owning the faved status
|
||||
TargetAccountID string `pg:",notnull"`
|
||||
TargetAccountID string `pg:",notnull"`
|
||||
// database id of the status that has been 'faved'
|
||||
StatusID string `pg:",notnull"`
|
||||
StatusID string `pg:",notnull"`
|
||||
|
||||
// FavedStatus is the status being interacted with. It won't be put or retrieved from the db, it's just for conveniently passing a pointer around.
|
||||
FavedStatus *Status `pg:"-"`
|
||||
}
|
||||
|
|
|
@ -497,12 +497,44 @@ func (ps *postgresService) NewSignup(username string, reason string, requireAppr
|
|||
}
|
||||
|
||||
func (ps *postgresService) SetHeaderOrAvatarForAccountID(mediaAttachment *gtsmodel.MediaAttachment, accountID string) error {
|
||||
_, err := ps.conn.Model(mediaAttachment).Insert()
|
||||
return err
|
||||
if mediaAttachment.Avatar && mediaAttachment.Header {
|
||||
return errors.New("one media attachment cannot be both header and avatar")
|
||||
}
|
||||
|
||||
var headerOrAVI string
|
||||
if mediaAttachment.Avatar {
|
||||
headerOrAVI = "avatar"
|
||||
} else if mediaAttachment.Header {
|
||||
headerOrAVI = "header"
|
||||
} else {
|
||||
return errors.New("given media attachment was neither a header nor an avatar")
|
||||
}
|
||||
|
||||
// TODO: there are probably more side effects here that need to be handled
|
||||
if _, err := ps.conn.Model(mediaAttachment).OnConflict("(id) DO UPDATE").Insert(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := ps.conn.Model(>smodel.Account{}).Set(fmt.Sprintf("%s_media_attachment_id = ?", headerOrAVI), mediaAttachment.ID).Where("id = ?", accountID).Update(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ps *postgresService) GetHeaderForAccountID(header *gtsmodel.MediaAttachment, accountID string) error {
|
||||
if err := ps.conn.Model(header).Where("account_id = ?", accountID).Where("header = ?", true).Select(); err != nil {
|
||||
acct := >smodel.Account{}
|
||||
if err := ps.conn.Model(acct).Where("id = ?", accountID).Select(); err != nil {
|
||||
if err == pg.ErrNoRows {
|
||||
return ErrNoEntries{}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
if acct.HeaderMediaAttachmentID == "" {
|
||||
return ErrNoEntries{}
|
||||
}
|
||||
|
||||
if err := ps.conn.Model(header).Where("id = ?", acct.HeaderMediaAttachmentID).Select(); err != nil {
|
||||
if err == pg.ErrNoRows {
|
||||
return ErrNoEntries{}
|
||||
}
|
||||
|
@ -512,7 +544,19 @@ func (ps *postgresService) GetHeaderForAccountID(header *gtsmodel.MediaAttachmen
|
|||
}
|
||||
|
||||
func (ps *postgresService) GetAvatarForAccountID(avatar *gtsmodel.MediaAttachment, accountID string) error {
|
||||
if err := ps.conn.Model(avatar).Where("account_id = ?", accountID).Where("avatar = ?", true).Select(); err != nil {
|
||||
acct := >smodel.Account{}
|
||||
if err := ps.conn.Model(acct).Where("id = ?", accountID).Select(); err != nil {
|
||||
if err == pg.ErrNoRows {
|
||||
return ErrNoEntries{}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
if acct.AvatarMediaAttachmentID == "" {
|
||||
return ErrNoEntries{}
|
||||
}
|
||||
|
||||
if err := ps.conn.Model(avatar).Where("id = ?", acct.AvatarMediaAttachmentID).Select(); err != nil {
|
||||
if err == pg.ErrNoRows {
|
||||
return ErrNoEntries{}
|
||||
}
|
||||
|
@ -806,6 +850,79 @@ func (ps *postgresService) StatusPinnedBy(status *gtsmodel.Status, accountID str
|
|||
return ps.conn.Model(>smodel.StatusPin{}).Where("status_id = ?", status.ID).Where("account_id = ?", accountID).Exists()
|
||||
}
|
||||
|
||||
func (ps *postgresService) FaveStatus(status *gtsmodel.Status, accountID string) (*gtsmodel.StatusFave, error) {
|
||||
// first check if a fave already exists, we can just return if so
|
||||
existingFave := >smodel.StatusFave{}
|
||||
err := ps.conn.Model(existingFave).Where("status_id = ?", status.ID).Where("account_id = ?", accountID).Select()
|
||||
if err == nil {
|
||||
// fave already exists so just return nothing at all
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// an error occurred so it might exist or not, we don't know
|
||||
if err != pg.ErrNoRows {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// it doesn't exist so create it
|
||||
newFave := >smodel.StatusFave{
|
||||
AccountID: accountID,
|
||||
TargetAccountID: status.AccountID,
|
||||
StatusID: status.ID,
|
||||
}
|
||||
if _, err = ps.conn.Model(newFave).Insert(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return newFave, nil
|
||||
}
|
||||
|
||||
func (ps *postgresService) UnfaveStatus(status *gtsmodel.Status, accountID string) (*gtsmodel.StatusFave, error) {
|
||||
// if a fave doesn't exist, we don't need to do anything
|
||||
existingFave := >smodel.StatusFave{}
|
||||
err := ps.conn.Model(existingFave).Where("status_id = ?", status.ID).Where("account_id = ?", accountID).Select()
|
||||
// the fave doesn't exist so return nothing at all
|
||||
if err == pg.ErrNoRows {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// an error occurred so it might exist or not, we don't know
|
||||
if err != nil && err != pg.ErrNoRows {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// the fave exists so remove it
|
||||
if _, err = ps.conn.Model(>smodel.StatusFave{}).Where("status_id = ?", status.ID).Where("account_id = ?", accountID).Delete(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return existingFave, nil
|
||||
}
|
||||
|
||||
func (ps *postgresService) WhoFavedStatus(status *gtsmodel.Status) ([]*gtsmodel.Account, error) {
|
||||
accounts := []*gtsmodel.Account{}
|
||||
|
||||
faves := []*gtsmodel.StatusFave{}
|
||||
if err := ps.conn.Model(&faves).Where("status_id = ?", status.ID).Select(); err != nil {
|
||||
if err == pg.ErrNoRows {
|
||||
return accounts, nil // no rows just means nobody has faved this status, so that's fine
|
||||
}
|
||||
return nil, err // an actual error has occurred
|
||||
}
|
||||
|
||||
for _, f := range faves {
|
||||
acc := >smodel.Account{}
|
||||
if err := ps.conn.Model(acc).Where("id = ?", f.AccountID).Select(); err != nil {
|
||||
if err == pg.ErrNoRows {
|
||||
continue // the account doesn't exist for some reason??? but this isn't the place to worry about that so just skip it
|
||||
}
|
||||
return nil, err // an actual error has occurred
|
||||
}
|
||||
accounts = append(accounts, acc)
|
||||
}
|
||||
return accounts, nil
|
||||
}
|
||||
|
||||
/*
|
||||
CONVERSION FUNCTIONS
|
||||
*/
|
||||
|
|
|
@ -170,8 +170,8 @@ func (c *converter) AccountToMastoPublic(a *gtsmodel.Account) (*mastotypes.Accou
|
|||
return nil, fmt.Errorf("error getting avatar: %s", err)
|
||||
}
|
||||
}
|
||||
aviURL := avi.File.Path
|
||||
aviURLStatic := avi.Thumbnail.Path
|
||||
aviURL := avi.URL
|
||||
aviURLStatic := avi.Thumbnail.URL
|
||||
|
||||
header := >smodel.MediaAttachment{}
|
||||
if err := c.db.GetHeaderForAccountID(avi, a.ID); err != nil {
|
||||
|
@ -179,8 +179,8 @@ func (c *converter) AccountToMastoPublic(a *gtsmodel.Account) (*mastotypes.Accou
|
|||
return nil, fmt.Errorf("error getting header: %s", err)
|
||||
}
|
||||
}
|
||||
headerURL := header.File.Path
|
||||
headerURLStatic := header.Thumbnail.Path
|
||||
headerURL := header.URL
|
||||
headerURLStatic := header.Thumbnail.URL
|
||||
|
||||
// get the fields set on this account
|
||||
fields := []mastotypes.Field{}
|
||||
|
|
|
@ -123,6 +123,12 @@ func StandardDBSetup(db db.DB) {
|
|||
}
|
||||
}
|
||||
|
||||
for _, v := range NewTestFaves() {
|
||||
if err := db.Put(v); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := db.CreateInstanceAccount(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 447 KiB |
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
|
@ -41,6 +41,16 @@ func NewTestTokens() map[string]*oauth.Token {
|
|||
AccessCreateAt: time.Now(),
|
||||
AccessExpiresAt: time.Now().Add(72 * time.Hour),
|
||||
},
|
||||
"local_account_2": {
|
||||
ID: "b04cae99-39b5-4610-a425-dc6b91c78a72",
|
||||
ClientID: "a4f6a2ea-a32b-4600-8853-72fc4ad98a1f",
|
||||
UserID: "d120bd97-866f-4a05-9690-a1294b9934c3",
|
||||
RedirectURI: "http://localhost:8080",
|
||||
Scope: "read write follow push",
|
||||
Access: "PIPINALKNNNFNF98717NAMNAMNFKIJKJ881818KJKJAKJJJA",
|
||||
AccessCreateAt: time.Now(),
|
||||
AccessExpiresAt: time.Now().Add(72 * time.Hour),
|
||||
},
|
||||
}
|
||||
return tokens
|
||||
}
|
||||
|
@ -243,184 +253,152 @@ func NewTestAccounts() map[string]*gtsmodel.Account {
|
|||
Username: "localhost:8080",
|
||||
},
|
||||
"unconfirmed_account": {
|
||||
ID: "59e197f5-87cd-4be8-ac7c-09082ccc4b4d",
|
||||
Username: "weed_lord420",
|
||||
AvatarFileName: "",
|
||||
AvatarContentType: "",
|
||||
AvatarFileSize: 0,
|
||||
AvatarUpdatedAt: time.Time{},
|
||||
AvatarRemoteURL: "",
|
||||
HeaderFileName: "",
|
||||
HeaderContentType: "",
|
||||
HeaderFileSize: 0,
|
||||
HeaderUpdatedAt: time.Time{},
|
||||
HeaderRemoteURL: "",
|
||||
DisplayName: "",
|
||||
Fields: []gtsmodel.Field{},
|
||||
Note: "",
|
||||
Memorial: false,
|
||||
MovedToAccountID: "",
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
Bot: false,
|
||||
Reason: "hi, please let me in! I'm looking for somewhere neato bombeato to hang out.",
|
||||
Locked: false,
|
||||
Discoverable: false,
|
||||
Privacy: gtsmodel.VisibilityPublic,
|
||||
Sensitive: false,
|
||||
Language: "en",
|
||||
URI: "http://localhost:8080/users/weed_lord420",
|
||||
URL: "http://localhost:8080/@weed_lord420",
|
||||
LastWebfingeredAt: time.Time{},
|
||||
InboxURL: "http://localhost:8080/users/weed_lord420/inbox",
|
||||
OutboxURL: "http://localhost:8080/users/weed_lord420/outbox",
|
||||
SharedInboxURL: "",
|
||||
FollowersURL: "http://localhost:8080/users/weed_lord420/followers",
|
||||
FeaturedCollectionURL: "http://localhost:8080/users/weed_lord420/collections/featured",
|
||||
ActorType: gtsmodel.ActivityStreamsPerson,
|
||||
AlsoKnownAs: "",
|
||||
PrivateKey: &rsa.PrivateKey{},
|
||||
PublicKey: &rsa.PublicKey{},
|
||||
SensitizedAt: time.Time{},
|
||||
SilencedAt: time.Time{},
|
||||
SuspendedAt: time.Time{},
|
||||
HideCollections: false,
|
||||
SuspensionOrigin: "",
|
||||
ID: "59e197f5-87cd-4be8-ac7c-09082ccc4b4d",
|
||||
Username: "weed_lord420",
|
||||
AvatarMediaAttachmentID: "",
|
||||
HeaderMediaAttachmentID: "",
|
||||
DisplayName: "",
|
||||
Fields: []gtsmodel.Field{},
|
||||
Note: "",
|
||||
Memorial: false,
|
||||
MovedToAccountID: "",
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
Bot: false,
|
||||
Reason: "hi, please let me in! I'm looking for somewhere neato bombeato to hang out.",
|
||||
Locked: false,
|
||||
Discoverable: false,
|
||||
Privacy: gtsmodel.VisibilityPublic,
|
||||
Sensitive: false,
|
||||
Language: "en",
|
||||
URI: "http://localhost:8080/users/weed_lord420",
|
||||
URL: "http://localhost:8080/@weed_lord420",
|
||||
LastWebfingeredAt: time.Time{},
|
||||
InboxURL: "http://localhost:8080/users/weed_lord420/inbox",
|
||||
OutboxURL: "http://localhost:8080/users/weed_lord420/outbox",
|
||||
SharedInboxURL: "",
|
||||
FollowersURL: "http://localhost:8080/users/weed_lord420/followers",
|
||||
FeaturedCollectionURL: "http://localhost:8080/users/weed_lord420/collections/featured",
|
||||
ActorType: gtsmodel.ActivityStreamsPerson,
|
||||
AlsoKnownAs: "",
|
||||
PrivateKey: &rsa.PrivateKey{},
|
||||
PublicKey: &rsa.PublicKey{},
|
||||
SensitizedAt: time.Time{},
|
||||
SilencedAt: time.Time{},
|
||||
SuspendedAt: time.Time{},
|
||||
HideCollections: false,
|
||||
SuspensionOrigin: "",
|
||||
},
|
||||
"admin_account": {
|
||||
ID: "8020dbb4-1e7b-4d99-a872-4cf94e64210f",
|
||||
Username: "admin",
|
||||
AvatarFileName: "",
|
||||
AvatarContentType: "",
|
||||
AvatarFileSize: 0,
|
||||
AvatarUpdatedAt: time.Time{},
|
||||
AvatarRemoteURL: "",
|
||||
HeaderFileName: "",
|
||||
HeaderContentType: "",
|
||||
HeaderFileSize: 0,
|
||||
HeaderUpdatedAt: time.Time{},
|
||||
HeaderRemoteURL: "",
|
||||
DisplayName: "",
|
||||
Fields: []gtsmodel.Field{},
|
||||
Note: "",
|
||||
Memorial: false,
|
||||
MovedToAccountID: "",
|
||||
CreatedAt: time.Now().Add(-72 * time.Hour),
|
||||
UpdatedAt: time.Now().Add(-72 * time.Hour),
|
||||
Bot: false,
|
||||
Reason: "",
|
||||
Locked: false,
|
||||
Discoverable: true,
|
||||
Privacy: gtsmodel.VisibilityPublic,
|
||||
Sensitive: false,
|
||||
Language: "en",
|
||||
URI: "http://localhost:8080/users/admin",
|
||||
URL: "http://localhost:8080/@admin",
|
||||
LastWebfingeredAt: time.Time{},
|
||||
InboxURL: "http://localhost:8080/users/admin/inbox",
|
||||
OutboxURL: "http://localhost:8080/users/admin/outbox",
|
||||
SharedInboxURL: "",
|
||||
FollowersURL: "http://localhost:8080/users/admin/followers",
|
||||
FeaturedCollectionURL: "http://localhost:8080/users/admin/collections/featured",
|
||||
ActorType: gtsmodel.ActivityStreamsPerson,
|
||||
AlsoKnownAs: "",
|
||||
PrivateKey: &rsa.PrivateKey{},
|
||||
PublicKey: &rsa.PublicKey{},
|
||||
SensitizedAt: time.Time{},
|
||||
SilencedAt: time.Time{},
|
||||
SuspendedAt: time.Time{},
|
||||
HideCollections: false,
|
||||
SuspensionOrigin: "",
|
||||
ID: "8020dbb4-1e7b-4d99-a872-4cf94e64210f",
|
||||
Username: "admin",
|
||||
AvatarMediaAttachmentID: "",
|
||||
HeaderMediaAttachmentID: "",
|
||||
DisplayName: "",
|
||||
Fields: []gtsmodel.Field{},
|
||||
Note: "",
|
||||
Memorial: false,
|
||||
MovedToAccountID: "",
|
||||
CreatedAt: time.Now().Add(-72 * time.Hour),
|
||||
UpdatedAt: time.Now().Add(-72 * time.Hour),
|
||||
Bot: false,
|
||||
Reason: "",
|
||||
Locked: false,
|
||||
Discoverable: true,
|
||||
Privacy: gtsmodel.VisibilityPublic,
|
||||
Sensitive: false,
|
||||
Language: "en",
|
||||
URI: "http://localhost:8080/users/admin",
|
||||
URL: "http://localhost:8080/@admin",
|
||||
LastWebfingeredAt: time.Time{},
|
||||
InboxURL: "http://localhost:8080/users/admin/inbox",
|
||||
OutboxURL: "http://localhost:8080/users/admin/outbox",
|
||||
SharedInboxURL: "",
|
||||
FollowersURL: "http://localhost:8080/users/admin/followers",
|
||||
FeaturedCollectionURL: "http://localhost:8080/users/admin/collections/featured",
|
||||
ActorType: gtsmodel.ActivityStreamsPerson,
|
||||
AlsoKnownAs: "",
|
||||
PrivateKey: &rsa.PrivateKey{},
|
||||
PublicKey: &rsa.PublicKey{},
|
||||
SensitizedAt: time.Time{},
|
||||
SilencedAt: time.Time{},
|
||||
SuspendedAt: time.Time{},
|
||||
HideCollections: false,
|
||||
SuspensionOrigin: "",
|
||||
},
|
||||
"local_account_1": {
|
||||
ID: "580072df-4d03-4684-a412-89fd6f7d77e6",
|
||||
Username: "the_mighty_zork",
|
||||
AvatarFileName: "http://localhost:8080/fileserver/media/580072df-4d03-4684-a412-89fd6f7d77e6/avatar/original/75cfbe52-a5fb-451b-8f5a-b023229dce8d.jpeg",
|
||||
AvatarContentType: "image/jpeg",
|
||||
AvatarFileSize: 0,
|
||||
AvatarUpdatedAt: time.Time{},
|
||||
AvatarRemoteURL: "",
|
||||
HeaderFileName: "http://localhost:8080/fileserver/media/580072df-4d03-4684-a412-89fd6f7d77e6/header/original/9651c1ed-c288-4063-a95c-c7f8ff2a633f.jpeg",
|
||||
HeaderContentType: "image/jpeg",
|
||||
HeaderFileSize: 0,
|
||||
HeaderUpdatedAt: time.Time{},
|
||||
HeaderRemoteURL: "",
|
||||
DisplayName: "original zork (he/they)",
|
||||
Fields: []gtsmodel.Field{},
|
||||
Note: "hey yo this is my profile!",
|
||||
Memorial: false,
|
||||
MovedToAccountID: "",
|
||||
CreatedAt: time.Now().Add(-48 * time.Hour),
|
||||
UpdatedAt: time.Now().Add(-48 * time.Hour),
|
||||
Bot: false,
|
||||
Reason: "I wanna be on this damned webbed site so bad! Please! Wow",
|
||||
Locked: false,
|
||||
Discoverable: true,
|
||||
Privacy: gtsmodel.VisibilityPublic,
|
||||
Sensitive: false,
|
||||
Language: "en",
|
||||
URI: "http://localhost:8080/users/the_mighty_zork",
|
||||
URL: "http://localhost:8080/@the_mighty_zork",
|
||||
LastWebfingeredAt: time.Time{},
|
||||
InboxURL: "http://localhost:8080/users/the_mighty_zork/inbox",
|
||||
OutboxURL: "http://localhost:8080/users/the_mighty_zork/outbox",
|
||||
SharedInboxURL: "",
|
||||
FollowersURL: "http://localhost:8080/users/the_mighty_zork/followers",
|
||||
FeaturedCollectionURL: "http://localhost:8080/users/the_mighty_zork/collections/featured",
|
||||
ActorType: gtsmodel.ActivityStreamsPerson,
|
||||
AlsoKnownAs: "",
|
||||
PrivateKey: &rsa.PrivateKey{},
|
||||
PublicKey: &rsa.PublicKey{},
|
||||
SensitizedAt: time.Time{},
|
||||
SilencedAt: time.Time{},
|
||||
SuspendedAt: time.Time{},
|
||||
HideCollections: false,
|
||||
SuspensionOrigin: "",
|
||||
ID: "580072df-4d03-4684-a412-89fd6f7d77e6",
|
||||
Username: "the_mighty_zork",
|
||||
AvatarMediaAttachmentID: "a849906f-8b8e-4b43-ac2f-6979ccbcd442",
|
||||
HeaderMediaAttachmentID: "",
|
||||
DisplayName: "original zork (he/they)",
|
||||
Fields: []gtsmodel.Field{},
|
||||
Note: "hey yo this is my profile!",
|
||||
Memorial: false,
|
||||
MovedToAccountID: "",
|
||||
CreatedAt: time.Now().Add(-48 * time.Hour),
|
||||
UpdatedAt: time.Now().Add(-48 * time.Hour),
|
||||
Bot: false,
|
||||
Reason: "I wanna be on this damned webbed site so bad! Please! Wow",
|
||||
Locked: false,
|
||||
Discoverable: true,
|
||||
Privacy: gtsmodel.VisibilityPublic,
|
||||
Sensitive: false,
|
||||
Language: "en",
|
||||
URI: "http://localhost:8080/users/the_mighty_zork",
|
||||
URL: "http://localhost:8080/@the_mighty_zork",
|
||||
LastWebfingeredAt: time.Time{},
|
||||
InboxURL: "http://localhost:8080/users/the_mighty_zork/inbox",
|
||||
OutboxURL: "http://localhost:8080/users/the_mighty_zork/outbox",
|
||||
SharedInboxURL: "",
|
||||
FollowersURL: "http://localhost:8080/users/the_mighty_zork/followers",
|
||||
FeaturedCollectionURL: "http://localhost:8080/users/the_mighty_zork/collections/featured",
|
||||
ActorType: gtsmodel.ActivityStreamsPerson,
|
||||
AlsoKnownAs: "",
|
||||
PrivateKey: &rsa.PrivateKey{},
|
||||
PublicKey: &rsa.PublicKey{},
|
||||
SensitizedAt: time.Time{},
|
||||
SilencedAt: time.Time{},
|
||||
SuspendedAt: time.Time{},
|
||||
HideCollections: false,
|
||||
SuspensionOrigin: "",
|
||||
},
|
||||
"local_account_2": {
|
||||
ID: "eecaad73-5703-426d-9312-276641daa31e",
|
||||
Username: "1happyturtle",
|
||||
AvatarFileName: "http://localhost:8080/fileserver/media/eecaad73-5703-426d-9312-276641daa31e/avatar/original/d5e7c265-91a6-4d84-8c27-7e1efe5720da.jpeg",
|
||||
AvatarContentType: "image/jpeg",
|
||||
AvatarFileSize: 0,
|
||||
AvatarUpdatedAt: time.Time{},
|
||||
AvatarRemoteURL: "",
|
||||
HeaderFileName: "http://localhost:8080/fileserver/media/eecaad73-5703-426d-9312-276641daa31e/header/original/e75d4117-21b6-4315-a428-eb3944235996.jpeg",
|
||||
HeaderContentType: "image/jpeg",
|
||||
HeaderFileSize: 0,
|
||||
HeaderUpdatedAt: time.Time{},
|
||||
HeaderRemoteURL: "",
|
||||
DisplayName: "happy little turtle :3",
|
||||
Fields: []gtsmodel.Field{},
|
||||
Note: "i post about things that concern me",
|
||||
Memorial: false,
|
||||
MovedToAccountID: "",
|
||||
CreatedAt: time.Now().Add(-190 * time.Hour),
|
||||
UpdatedAt: time.Now().Add(-36 * time.Hour),
|
||||
Bot: false,
|
||||
Reason: "",
|
||||
Locked: true,
|
||||
Discoverable: false,
|
||||
Privacy: gtsmodel.VisibilityFollowersOnly,
|
||||
Sensitive: false,
|
||||
Language: "en",
|
||||
URI: "http://localhost:8080/users/1happyturtle",
|
||||
URL: "http://localhost:8080/@1happyturtle",
|
||||
LastWebfingeredAt: time.Time{},
|
||||
InboxURL: "http://localhost:8080/users/1happyturtle/inbox",
|
||||
OutboxURL: "http://localhost:8080/users/1happyturtle/outbox",
|
||||
SharedInboxURL: "",
|
||||
FollowersURL: "http://localhost:8080/users/1happyturtle/followers",
|
||||
FeaturedCollectionURL: "http://localhost:8080/users/1happyturtle/collections/featured",
|
||||
ActorType: gtsmodel.ActivityStreamsPerson,
|
||||
AlsoKnownAs: "",
|
||||
PrivateKey: &rsa.PrivateKey{},
|
||||
PublicKey: &rsa.PublicKey{},
|
||||
SensitizedAt: time.Time{},
|
||||
SilencedAt: time.Time{},
|
||||
SuspendedAt: time.Time{},
|
||||
HideCollections: false,
|
||||
SuspensionOrigin: "",
|
||||
ID: "eecaad73-5703-426d-9312-276641daa31e",
|
||||
Username: "1happyturtle",
|
||||
AvatarMediaAttachmentID: "",
|
||||
HeaderMediaAttachmentID: "",
|
||||
DisplayName: "happy little turtle :3",
|
||||
Fields: []gtsmodel.Field{},
|
||||
Note: "i post about things that concern me",
|
||||
Memorial: false,
|
||||
MovedToAccountID: "",
|
||||
CreatedAt: time.Now().Add(-190 * time.Hour),
|
||||
UpdatedAt: time.Now().Add(-36 * time.Hour),
|
||||
Bot: false,
|
||||
Reason: "",
|
||||
Locked: true,
|
||||
Discoverable: false,
|
||||
Privacy: gtsmodel.VisibilityFollowersOnly,
|
||||
Sensitive: false,
|
||||
Language: "en",
|
||||
URI: "http://localhost:8080/users/1happyturtle",
|
||||
URL: "http://localhost:8080/@1happyturtle",
|
||||
LastWebfingeredAt: time.Time{},
|
||||
InboxURL: "http://localhost:8080/users/1happyturtle/inbox",
|
||||
OutboxURL: "http://localhost:8080/users/1happyturtle/outbox",
|
||||
SharedInboxURL: "",
|
||||
FollowersURL: "http://localhost:8080/users/1happyturtle/followers",
|
||||
FeaturedCollectionURL: "http://localhost:8080/users/1happyturtle/collections/featured",
|
||||
ActorType: gtsmodel.ActivityStreamsPerson,
|
||||
AlsoKnownAs: "",
|
||||
PrivateKey: &rsa.PrivateKey{},
|
||||
PublicKey: &rsa.PublicKey{},
|
||||
SensitizedAt: time.Time{},
|
||||
SilencedAt: time.Time{},
|
||||
SuspendedAt: time.Time{},
|
||||
HideCollections: false,
|
||||
SuspensionOrigin: "",
|
||||
},
|
||||
"remote_account_1": {
|
||||
ID: "c2c6e647-e2a9-4286-883b-e4a188186664",
|
||||
|
@ -643,9 +621,58 @@ func NewTestAttachments() map[string]*gtsmodel.MediaAttachment {
|
|||
Avatar: false,
|
||||
Header: false,
|
||||
},
|
||||
"local_account_1_avatar": {
|
||||
ID: "a849906f-8b8e-4b43-ac2f-6979ccbcd442",
|
||||
StatusID: "", // this attachment isn't connected to a status
|
||||
URL: "http://localhost:8080/fileserver/580072df-4d03-4684-a412-89fd6f7d77e6/avatar/original/a849906f-8b8e-4b43-ac2f-6979ccbcd442.jpeg",
|
||||
RemoteURL: "",
|
||||
CreatedAt: time.Now().Add(47 * time.Hour),
|
||||
UpdatedAt: time.Now().Add(47 * time.Hour),
|
||||
Type: gtsmodel.FileTypeImage,
|
||||
FileMeta: gtsmodel.FileMeta{
|
||||
Original: gtsmodel.Original{
|
||||
Width: 1092,
|
||||
Height: 1800,
|
||||
Size: 1965600,
|
||||
Aspect: 0.6066666666666667,
|
||||
},
|
||||
Small: gtsmodel.Small{
|
||||
Width: 155,
|
||||
Height: 256,
|
||||
Size: 39680,
|
||||
Aspect: 0.60546875,
|
||||
},
|
||||
Focus: gtsmodel.Focus{
|
||||
X: 0,
|
||||
Y: 0,
|
||||
},
|
||||
},
|
||||
AccountID: "580072df-4d03-4684-a412-89fd6f7d77e6",
|
||||
Description: "a green goblin looking nasty",
|
||||
ScheduledStatusID: "",
|
||||
Blurhash: "LKK9MT,p|YSNDkJ-5rsmvnwcOoe:",
|
||||
Processing: 2,
|
||||
File: gtsmodel.File{
|
||||
Path: "/gotosocial/storage/580072df-4d03-4684-a412-89fd6f7d77e6/avatar/original/a849906f-8b8e-4b43-ac2f-6979ccbcd442.jpeg",
|
||||
ContentType: "image/jpeg",
|
||||
FileSize: 457680,
|
||||
UpdatedAt: time.Now().Add(47 * time.Hour),
|
||||
},
|
||||
Thumbnail: gtsmodel.Thumbnail{
|
||||
Path: "/gotosocial/storage/580072df-4d03-4684-a412-89fd6f7d77e6/avatar/small/a849906f-8b8e-4b43-ac2f-6979ccbcd442.jpeg",
|
||||
ContentType: "image/jpeg",
|
||||
FileSize: 15374,
|
||||
UpdatedAt: time.Now().Add(47 * time.Hour),
|
||||
URL: "http://localhost:8080/fileserver/580072df-4d03-4684-a412-89fd6f7d77e6/avatar/small/a849906f-8b8e-4b43-ac2f-6979ccbcd442.jpeg",
|
||||
RemoteURL: "",
|
||||
},
|
||||
Avatar: true,
|
||||
Header: false,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// NewTestEmojis returns a map of gts emojis, keyed by the emoji shortcode
|
||||
func NewTestEmojis() map[string]*gtsmodel.Emoji {
|
||||
return map[string]*gtsmodel.Emoji{
|
||||
"rainbow": {
|
||||
|
@ -693,9 +720,14 @@ func NewTestStoredAttachments() map[string]filenames {
|
|||
original: "ohyou-original.jpeg",
|
||||
small: "ohyou-small.jpeg",
|
||||
},
|
||||
"local_account_1_avatar": {
|
||||
original: "zork-original.jpeg",
|
||||
small: "zork-small.jpeg",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// NewtestStoredEmoji returns a map of filenames, keyed according to which emoji they pertain to
|
||||
func NewTestStoredEmoji() map[string]filenames {
|
||||
return map[string]filenames{
|
||||
"rainbow": {
|
||||
|
@ -710,24 +742,24 @@ func NewTestStoredEmoji() map[string]filenames {
|
|||
func NewTestStatuses() map[string]*gtsmodel.Status {
|
||||
return map[string]*gtsmodel.Status{
|
||||
"admin_account_status_1": {
|
||||
ID: "502ccd6f-0edf-48d7-9016-2dfa4d3714cd",
|
||||
URI: "http://localhost:8080/users/admin/statuses/502ccd6f-0edf-48d7-9016-2dfa4d3714cd",
|
||||
URL: "http://localhost:8080/@admin/statuses/502ccd6f-0edf-48d7-9016-2dfa4d3714cd",
|
||||
Content: "hello world! #welcome ! first post on the instance :rainbow: !",
|
||||
Attachments: []string{"b052241b-f30f-4dc6-92fc-2bad0be1f8d8"},
|
||||
Tags: []string{"a7e8f5ca-88a1-4652-8079-a187eab8d56e"},
|
||||
Mentions: []string{},
|
||||
Emojis: []string{"a96ec4f3-1cae-47e4-a508-f9d66a6b221b"},
|
||||
CreatedAt: time.Now().Add(-71 * time.Hour),
|
||||
UpdatedAt: time.Now().Add(-71 * time.Hour),
|
||||
Local: true,
|
||||
AccountID: "8020dbb4-1e7b-4d99-a872-4cf94e64210f",
|
||||
InReplyToID: "",
|
||||
BoostOfID: "",
|
||||
ContentWarning: "",
|
||||
Visibility: gtsmodel.VisibilityPublic,
|
||||
Sensitive: false,
|
||||
Language: "en",
|
||||
ID: "502ccd6f-0edf-48d7-9016-2dfa4d3714cd",
|
||||
URI: "http://localhost:8080/users/admin/statuses/502ccd6f-0edf-48d7-9016-2dfa4d3714cd",
|
||||
URL: "http://localhost:8080/@admin/statuses/502ccd6f-0edf-48d7-9016-2dfa4d3714cd",
|
||||
Content: "hello world! #welcome ! first post on the instance :rainbow: !",
|
||||
Attachments: []string{"b052241b-f30f-4dc6-92fc-2bad0be1f8d8"},
|
||||
Tags: []string{"a7e8f5ca-88a1-4652-8079-a187eab8d56e"},
|
||||
Mentions: []string{},
|
||||
Emojis: []string{"a96ec4f3-1cae-47e4-a508-f9d66a6b221b"},
|
||||
CreatedAt: time.Now().Add(-71 * time.Hour),
|
||||
UpdatedAt: time.Now().Add(-71 * time.Hour),
|
||||
Local: true,
|
||||
AccountID: "8020dbb4-1e7b-4d99-a872-4cf94e64210f",
|
||||
InReplyToID: "",
|
||||
BoostOfID: "",
|
||||
ContentWarning: "",
|
||||
Visibility: gtsmodel.VisibilityPublic,
|
||||
Sensitive: false,
|
||||
Language: "en",
|
||||
CreatedWithApplicationID: "9bf9e368-037f-444d-8ffd-1091d1c21c4c",
|
||||
VisibilityAdvanced: >smodel.VisibilityAdvanced{
|
||||
Federated: true,
|
||||
|
@ -738,20 +770,20 @@ func NewTestStatuses() map[string]*gtsmodel.Status {
|
|||
ActivityStreamsType: gtsmodel.ActivityStreamsNote,
|
||||
},
|
||||
"admin_account_status_2": {
|
||||
ID: "0fb3f1ac-5cd8-48ac-9050-3d95dc7e44e9",
|
||||
URI: "http://localhost:8080/users/admin/statuses/0fb3f1ac-5cd8-48ac-9050-3d95dc7e44e9",
|
||||
URL: "http://localhost:8080/@admin/statuses/0fb3f1ac-5cd8-48ac-9050-3d95dc7e44e9",
|
||||
Content: "🐕🐕🐕🐕🐕",
|
||||
CreatedAt: time.Now().Add(-70 * time.Hour),
|
||||
UpdatedAt: time.Now().Add(-70 * time.Hour),
|
||||
Local: true,
|
||||
AccountID: "8020dbb4-1e7b-4d99-a872-4cf94e64210f",
|
||||
InReplyToID: "",
|
||||
BoostOfID: "",
|
||||
ContentWarning: "open to see some puppies",
|
||||
Visibility: gtsmodel.VisibilityPublic,
|
||||
Sensitive: true,
|
||||
Language: "en",
|
||||
ID: "0fb3f1ac-5cd8-48ac-9050-3d95dc7e44e9",
|
||||
URI: "http://localhost:8080/users/admin/statuses/0fb3f1ac-5cd8-48ac-9050-3d95dc7e44e9",
|
||||
URL: "http://localhost:8080/@admin/statuses/0fb3f1ac-5cd8-48ac-9050-3d95dc7e44e9",
|
||||
Content: "🐕🐕🐕🐕🐕",
|
||||
CreatedAt: time.Now().Add(-70 * time.Hour),
|
||||
UpdatedAt: time.Now().Add(-70 * time.Hour),
|
||||
Local: true,
|
||||
AccountID: "8020dbb4-1e7b-4d99-a872-4cf94e64210f",
|
||||
InReplyToID: "",
|
||||
BoostOfID: "",
|
||||
ContentWarning: "open to see some puppies",
|
||||
Visibility: gtsmodel.VisibilityPublic,
|
||||
Sensitive: true,
|
||||
Language: "en",
|
||||
CreatedWithApplicationID: "9bf9e368-037f-444d-8ffd-1091d1c21c4c",
|
||||
VisibilityAdvanced: >smodel.VisibilityAdvanced{
|
||||
Federated: true,
|
||||
|
@ -762,20 +794,20 @@ func NewTestStatuses() map[string]*gtsmodel.Status {
|
|||
ActivityStreamsType: gtsmodel.ActivityStreamsNote,
|
||||
},
|
||||
"local_account_1_status_1": {
|
||||
ID: "91b1e795-74ff-4672-a4c4-476616710e2d",
|
||||
URI: "http://localhost:8080/users/the_mighty_zork/statuses/91b1e795-74ff-4672-a4c4-476616710e2d",
|
||||
URL: "http://localhost:8080/@the_mighty_zork/statuses/91b1e795-74ff-4672-a4c4-476616710e2d",
|
||||
Content: "hello everyone!",
|
||||
CreatedAt: time.Now().Add(-47 * time.Hour),
|
||||
UpdatedAt: time.Now().Add(-47 * time.Hour),
|
||||
Local: true,
|
||||
AccountID: "580072df-4d03-4684-a412-89fd6f7d77e6",
|
||||
InReplyToID: "",
|
||||
BoostOfID: "",
|
||||
ContentWarning: "introduction post",
|
||||
Visibility: gtsmodel.VisibilityPublic,
|
||||
Sensitive: true,
|
||||
Language: "en",
|
||||
ID: "91b1e795-74ff-4672-a4c4-476616710e2d",
|
||||
URI: "http://localhost:8080/users/the_mighty_zork/statuses/91b1e795-74ff-4672-a4c4-476616710e2d",
|
||||
URL: "http://localhost:8080/@the_mighty_zork/statuses/91b1e795-74ff-4672-a4c4-476616710e2d",
|
||||
Content: "hello everyone!",
|
||||
CreatedAt: time.Now().Add(-47 * time.Hour),
|
||||
UpdatedAt: time.Now().Add(-47 * time.Hour),
|
||||
Local: true,
|
||||
AccountID: "580072df-4d03-4684-a412-89fd6f7d77e6",
|
||||
InReplyToID: "",
|
||||
BoostOfID: "",
|
||||
ContentWarning: "introduction post",
|
||||
Visibility: gtsmodel.VisibilityPublic,
|
||||
Sensitive: true,
|
||||
Language: "en",
|
||||
CreatedWithApplicationID: "f88697b8-ee3d-46c2-ac3f-dbb85566c3cc",
|
||||
VisibilityAdvanced: >smodel.VisibilityAdvanced{
|
||||
Federated: true,
|
||||
|
@ -786,20 +818,20 @@ func NewTestStatuses() map[string]*gtsmodel.Status {
|
|||
ActivityStreamsType: gtsmodel.ActivityStreamsNote,
|
||||
},
|
||||
"local_account_1_status_2": {
|
||||
ID: "3dd328d9-8bb1-48f5-bc96-5ccc1c696b4c",
|
||||
URI: "http://localhost:8080/users/the_mighty_zork/statuses/3dd328d9-8bb1-48f5-bc96-5ccc1c696b4c",
|
||||
URL: "http://localhost:8080/@the_mighty_zork/statuses/3dd328d9-8bb1-48f5-bc96-5ccc1c696b4c",
|
||||
Content: "this is an unlocked local-only post that shouldn't federate, but it's still boostable, replyable, and likeable",
|
||||
CreatedAt: time.Now().Add(-46 * time.Hour),
|
||||
UpdatedAt: time.Now().Add(-46 * time.Hour),
|
||||
Local: true,
|
||||
AccountID: "580072df-4d03-4684-a412-89fd6f7d77e6",
|
||||
InReplyToID: "",
|
||||
BoostOfID: "",
|
||||
ContentWarning: "",
|
||||
Visibility: gtsmodel.VisibilityUnlocked,
|
||||
Sensitive: false,
|
||||
Language: "en",
|
||||
ID: "3dd328d9-8bb1-48f5-bc96-5ccc1c696b4c",
|
||||
URI: "http://localhost:8080/users/the_mighty_zork/statuses/3dd328d9-8bb1-48f5-bc96-5ccc1c696b4c",
|
||||
URL: "http://localhost:8080/@the_mighty_zork/statuses/3dd328d9-8bb1-48f5-bc96-5ccc1c696b4c",
|
||||
Content: "this is an unlocked local-only post that shouldn't federate, but it's still boostable, replyable, and likeable",
|
||||
CreatedAt: time.Now().Add(-46 * time.Hour),
|
||||
UpdatedAt: time.Now().Add(-46 * time.Hour),
|
||||
Local: true,
|
||||
AccountID: "580072df-4d03-4684-a412-89fd6f7d77e6",
|
||||
InReplyToID: "",
|
||||
BoostOfID: "",
|
||||
ContentWarning: "",
|
||||
Visibility: gtsmodel.VisibilityUnlocked,
|
||||
Sensitive: false,
|
||||
Language: "en",
|
||||
CreatedWithApplicationID: "f88697b8-ee3d-46c2-ac3f-dbb85566c3cc",
|
||||
VisibilityAdvanced: >smodel.VisibilityAdvanced{
|
||||
Federated: false,
|
||||
|
@ -810,20 +842,20 @@ func NewTestStatuses() map[string]*gtsmodel.Status {
|
|||
ActivityStreamsType: gtsmodel.ActivityStreamsNote,
|
||||
},
|
||||
"local_account_1_status_3": {
|
||||
ID: "5e41963f-8ab9-4147-9f00-52d56e19da65",
|
||||
URI: "http://localhost:8080/users/the_mighty_zork/statuses/5e41963f-8ab9-4147-9f00-52d56e19da65",
|
||||
URL: "http://localhost:8080/@the_mighty_zork/statuses/5e41963f-8ab9-4147-9f00-52d56e19da65",
|
||||
Content: "this is a very personal post that I don't want anyone to interact with at all, and i only want mutuals to see it",
|
||||
CreatedAt: time.Now().Add(-45 * time.Hour),
|
||||
UpdatedAt: time.Now().Add(-45 * time.Hour),
|
||||
Local: true,
|
||||
AccountID: "580072df-4d03-4684-a412-89fd6f7d77e6",
|
||||
InReplyToID: "",
|
||||
BoostOfID: "",
|
||||
ContentWarning: "test: you shouldn't be able to interact with this post in any way",
|
||||
Visibility: gtsmodel.VisibilityMutualsOnly,
|
||||
Sensitive: false,
|
||||
Language: "en",
|
||||
ID: "5e41963f-8ab9-4147-9f00-52d56e19da65",
|
||||
URI: "http://localhost:8080/users/the_mighty_zork/statuses/5e41963f-8ab9-4147-9f00-52d56e19da65",
|
||||
URL: "http://localhost:8080/@the_mighty_zork/statuses/5e41963f-8ab9-4147-9f00-52d56e19da65",
|
||||
Content: "this is a very personal post that I don't want anyone to interact with at all, and i only want mutuals to see it",
|
||||
CreatedAt: time.Now().Add(-45 * time.Hour),
|
||||
UpdatedAt: time.Now().Add(-45 * time.Hour),
|
||||
Local: true,
|
||||
AccountID: "580072df-4d03-4684-a412-89fd6f7d77e6",
|
||||
InReplyToID: "",
|
||||
BoostOfID: "",
|
||||
ContentWarning: "test: you shouldn't be able to interact with this post in any way",
|
||||
Visibility: gtsmodel.VisibilityMutualsOnly,
|
||||
Sensitive: false,
|
||||
Language: "en",
|
||||
CreatedWithApplicationID: "f88697b8-ee3d-46c2-ac3f-dbb85566c3cc",
|
||||
VisibilityAdvanced: >smodel.VisibilityAdvanced{
|
||||
Federated: true,
|
||||
|
@ -834,21 +866,21 @@ func NewTestStatuses() map[string]*gtsmodel.Status {
|
|||
ActivityStreamsType: gtsmodel.ActivityStreamsNote,
|
||||
},
|
||||
"local_account_1_status_4": {
|
||||
ID: "18524c05-97dc-46d7-b474-c811bd9e1e32",
|
||||
URI: "http://localhost:8080/users/the_mighty_zork/statuses/18524c05-97dc-46d7-b474-c811bd9e1e32",
|
||||
URL: "http://localhost:8080/@the_mighty_zork/statuses/18524c05-97dc-46d7-b474-c811bd9e1e32",
|
||||
Content: "here's a little gif of trent",
|
||||
Attachments: []string{"510f6033-798b-4390-81b1-c38ca2205ad3"},
|
||||
CreatedAt: time.Now().Add(-1 * time.Hour),
|
||||
UpdatedAt: time.Now().Add(-1 * time.Hour),
|
||||
Local: true,
|
||||
AccountID: "580072df-4d03-4684-a412-89fd6f7d77e6",
|
||||
InReplyToID: "",
|
||||
BoostOfID: "",
|
||||
ContentWarning: "eye contact, trent reznor gif",
|
||||
Visibility: gtsmodel.VisibilityMutualsOnly,
|
||||
Sensitive: false,
|
||||
Language: "en",
|
||||
ID: "18524c05-97dc-46d7-b474-c811bd9e1e32",
|
||||
URI: "http://localhost:8080/users/the_mighty_zork/statuses/18524c05-97dc-46d7-b474-c811bd9e1e32",
|
||||
URL: "http://localhost:8080/@the_mighty_zork/statuses/18524c05-97dc-46d7-b474-c811bd9e1e32",
|
||||
Content: "here's a little gif of trent",
|
||||
Attachments: []string{"510f6033-798b-4390-81b1-c38ca2205ad3"},
|
||||
CreatedAt: time.Now().Add(-1 * time.Hour),
|
||||
UpdatedAt: time.Now().Add(-1 * time.Hour),
|
||||
Local: true,
|
||||
AccountID: "580072df-4d03-4684-a412-89fd6f7d77e6",
|
||||
InReplyToID: "",
|
||||
BoostOfID: "",
|
||||
ContentWarning: "eye contact, trent reznor gif",
|
||||
Visibility: gtsmodel.VisibilityMutualsOnly,
|
||||
Sensitive: false,
|
||||
Language: "en",
|
||||
CreatedWithApplicationID: "f88697b8-ee3d-46c2-ac3f-dbb85566c3cc",
|
||||
VisibilityAdvanced: >smodel.VisibilityAdvanced{
|
||||
Federated: true,
|
||||
|
@ -859,20 +891,20 @@ func NewTestStatuses() map[string]*gtsmodel.Status {
|
|||
ActivityStreamsType: gtsmodel.ActivityStreamsNote,
|
||||
},
|
||||
"local_account_2_status_1": {
|
||||
ID: "8945ccf2-3873-45e9-aa13-fd7163f19775",
|
||||
URI: "http://localhost:8080/users/1happyturtle/statuses/8945ccf2-3873-45e9-aa13-fd7163f19775",
|
||||
URL: "http://localhost:8080/@1happyturtle/statuses/8945ccf2-3873-45e9-aa13-fd7163f19775",
|
||||
Content: "🐢 hi everyone i post about turtles 🐢",
|
||||
CreatedAt: time.Now().Add(-189 * time.Hour),
|
||||
UpdatedAt: time.Now().Add(-189 * time.Hour),
|
||||
Local: true,
|
||||
AccountID: "eecaad73-5703-426d-9312-276641daa31e",
|
||||
InReplyToID: "",
|
||||
BoostOfID: "",
|
||||
ContentWarning: "introduction post",
|
||||
Visibility: gtsmodel.VisibilityPublic,
|
||||
Sensitive: true,
|
||||
Language: "en",
|
||||
ID: "8945ccf2-3873-45e9-aa13-fd7163f19775",
|
||||
URI: "http://localhost:8080/users/1happyturtle/statuses/8945ccf2-3873-45e9-aa13-fd7163f19775",
|
||||
URL: "http://localhost:8080/@1happyturtle/statuses/8945ccf2-3873-45e9-aa13-fd7163f19775",
|
||||
Content: "🐢 hi everyone i post about turtles 🐢",
|
||||
CreatedAt: time.Now().Add(-189 * time.Hour),
|
||||
UpdatedAt: time.Now().Add(-189 * time.Hour),
|
||||
Local: true,
|
||||
AccountID: "eecaad73-5703-426d-9312-276641daa31e",
|
||||
InReplyToID: "",
|
||||
BoostOfID: "",
|
||||
ContentWarning: "introduction post",
|
||||
Visibility: gtsmodel.VisibilityPublic,
|
||||
Sensitive: true,
|
||||
Language: "en",
|
||||
CreatedWithApplicationID: "6b0cd164-8497-4cd5-bec9-957886fac5df",
|
||||
VisibilityAdvanced: >smodel.VisibilityAdvanced{
|
||||
Federated: true,
|
||||
|
@ -883,20 +915,20 @@ func NewTestStatuses() map[string]*gtsmodel.Status {
|
|||
ActivityStreamsType: gtsmodel.ActivityStreamsNote,
|
||||
},
|
||||
"local_account_2_status_2": {
|
||||
ID: "c7e25a86-f0d3-4705-a73c-c597f687d3dd",
|
||||
URI: "http://localhost:8080/users/1happyturtle/statuses/c7e25a86-f0d3-4705-a73c-c597f687d3dd",
|
||||
URL: "http://localhost:8080/@1happyturtle/statuses/c7e25a86-f0d3-4705-a73c-c597f687d3dd",
|
||||
Content: "🐢 this one is federated, likeable, and boostable but not replyable 🐢",
|
||||
CreatedAt: time.Now().Add(-1 * time.Minute),
|
||||
UpdatedAt: time.Now().Add(-1 * time.Minute),
|
||||
Local: true,
|
||||
AccountID: "eecaad73-5703-426d-9312-276641daa31e",
|
||||
InReplyToID: "",
|
||||
BoostOfID: "",
|
||||
ContentWarning: "",
|
||||
Visibility: gtsmodel.VisibilityPublic,
|
||||
Sensitive: true,
|
||||
Language: "en",
|
||||
ID: "c7e25a86-f0d3-4705-a73c-c597f687d3dd",
|
||||
URI: "http://localhost:8080/users/1happyturtle/statuses/c7e25a86-f0d3-4705-a73c-c597f687d3dd",
|
||||
URL: "http://localhost:8080/@1happyturtle/statuses/c7e25a86-f0d3-4705-a73c-c597f687d3dd",
|
||||
Content: "🐢 this one is federated, likeable, and boostable but not replyable 🐢",
|
||||
CreatedAt: time.Now().Add(-1 * time.Minute),
|
||||
UpdatedAt: time.Now().Add(-1 * time.Minute),
|
||||
Local: true,
|
||||
AccountID: "eecaad73-5703-426d-9312-276641daa31e",
|
||||
InReplyToID: "",
|
||||
BoostOfID: "",
|
||||
ContentWarning: "",
|
||||
Visibility: gtsmodel.VisibilityPublic,
|
||||
Sensitive: true,
|
||||
Language: "en",
|
||||
CreatedWithApplicationID: "6b0cd164-8497-4cd5-bec9-957886fac5df",
|
||||
VisibilityAdvanced: >smodel.VisibilityAdvanced{
|
||||
Federated: true,
|
||||
|
@ -906,9 +938,34 @@ func NewTestStatuses() map[string]*gtsmodel.Status {
|
|||
},
|
||||
ActivityStreamsType: gtsmodel.ActivityStreamsNote,
|
||||
},
|
||||
"local_account_2_status_3": {
|
||||
ID: "75960e30-7a8e-4f45-87fa-440a4d1c9572",
|
||||
URI: "http://localhost:8080/users/1happyturtle/statuses/75960e30-7a8e-4f45-87fa-440a4d1c9572",
|
||||
URL: "http://localhost:8080/@1happyturtle/statuses/75960e30-7a8e-4f45-87fa-440a4d1c9572",
|
||||
Content: "🐢 i don't mind people sharing this one but I don't want likes or replies to it because cba🐢",
|
||||
CreatedAt: time.Now().Add(-2 * time.Minute),
|
||||
UpdatedAt: time.Now().Add(-2 * time.Minute),
|
||||
Local: true,
|
||||
AccountID: "eecaad73-5703-426d-9312-276641daa31e",
|
||||
InReplyToID: "",
|
||||
BoostOfID: "",
|
||||
ContentWarning: "you won't be able to like or reply to this",
|
||||
Visibility: gtsmodel.VisibilityUnlocked,
|
||||
Sensitive: true,
|
||||
Language: "en",
|
||||
CreatedWithApplicationID: "6b0cd164-8497-4cd5-bec9-957886fac5df",
|
||||
VisibilityAdvanced: >smodel.VisibilityAdvanced{
|
||||
Federated: true,
|
||||
Boostable: true,
|
||||
Replyable: false,
|
||||
Likeable: false,
|
||||
},
|
||||
ActivityStreamsType: gtsmodel.ActivityStreamsNote,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// NewTestTags returns a map of gts model tags keyed by their name
|
||||
func NewTestTags() map[string]*gtsmodel.Tag {
|
||||
return map[string]*gtsmodel.Tag{
|
||||
"welcome": {
|
||||
|
@ -923,3 +980,16 @@ func NewTestTags() map[string]*gtsmodel.Tag {
|
|||
},
|
||||
}
|
||||
}
|
||||
|
||||
// NewTestFaves returns a map of gts model faves, keyed in the format [faving_account]_[target_status]
|
||||
func NewTestFaves() map[string]*gtsmodel.StatusFave {
|
||||
return map[string]*gtsmodel.StatusFave{
|
||||
"local_account_1_admin_account_status_1": {
|
||||
ID: "fc4d42ef-631c-4125-bd9d-88695131284c",
|
||||
CreatedAt: time.Now().Add(-47 * time.Hour),
|
||||
AccountID: "580072df-4d03-4684-a412-89fd6f7d77e6", // local account 1
|
||||
TargetAccountID: "8020dbb4-1e7b-4d99-a872-4cf94e64210f", // admin account
|
||||
StatusID: "502ccd6f-0edf-48d7-9016-2dfa4d3714cd", // admin account status 1
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue