mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2025-11-01 17:42:25 -05:00
some tidying, some favedby, the usual
This commit is contained in:
parent
ddfb9aae65
commit
c92a72fdea
19 changed files with 1550 additions and 353 deletions
|
|
@ -74,9 +74,9 @@
|
||||||
* [x] /api/v1/statuses/:id DELETE (Delete a status)
|
* [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/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/reblogged_by GET (See who has reblogged a status)
|
||||||
* [ ] /api/v1/statuses/:id/favourited_by GET (See who has faved a status)
|
* [x] /api/v1/statuses/:id/favourited_by GET (See who has faved a status)
|
||||||
* [ ] /api/v1/statuses/:id/favourite POST (Fave a status)
|
* [x] /api/v1/statuses/:id/favourite POST (Fave a status)
|
||||||
* [ ] /api/v1/statuses/:id/favourite POST (Unfave 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/reblog POST (Reblog a status)
|
||||||
* [ ] /api/v1/statuses/:id/unreblog POST (Undo a reblog)
|
* [ ] /api/v1/statuses/:id/unreblog POST (Undo a reblog)
|
||||||
* [ ] /api/v1/statuses/:id/bookmark POST (Bookmark a status)
|
* [ ] /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 {
|
func (m *accountModule) Route(r router.Router) error {
|
||||||
r.AttachHandler(http.MethodPost, basePath, m.accountCreatePOSTHandler)
|
r.AttachHandler(http.MethodPost, basePath, m.accountCreatePOSTHandler)
|
||||||
r.AttachHandler(http.MethodGet, basePathWithID, m.muxHandler)
|
r.AttachHandler(http.MethodGet, basePathWithID, m.muxHandler)
|
||||||
|
r.AttachHandler(http.MethodPatch, basePathWithID, m.muxHandler)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -94,11 +95,16 @@ func (m *accountModule) CreateTables(db db.DB) error {
|
||||||
|
|
||||||
func (m *accountModule) muxHandler(c *gin.Context) {
|
func (m *accountModule) muxHandler(c *gin.Context) {
|
||||||
ru := c.Request.RequestURI
|
ru := c.Request.RequestURI
|
||||||
if strings.HasPrefix(ru, verifyPath) {
|
switch c.Request.Method {
|
||||||
m.accountVerifyGETHandler(c)
|
case http.MethodGet:
|
||||||
} else if strings.HasPrefix(ru, updateCredentialsPath) {
|
if strings.HasPrefix(ru, verifyPath) {
|
||||||
m.accountUpdateCredentialsPATCHHandler(c)
|
m.accountVerifyGETHandler(c)
|
||||||
} else {
|
} else {
|
||||||
m.accountGETHandler(c)
|
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
|
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
|
// finally we can return with all the information we derived above
|
||||||
c.DataFromReader(http.StatusOK, int64(contentLength), contentType, bytes.NewReader(attachmentBytes), map[string]string{})
|
c.DataFromReader(http.StatusOK, int64(contentLength), contentType, bytes.NewReader(attachmentBytes), map[string]string{})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,21 +36,28 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
IDKey = "id"
|
IDKey = "id"
|
||||||
BasePath = "/api/v1/statuses"
|
BasePath = "/api/v1/statuses"
|
||||||
BasePathWithID = BasePath + "/:" + IDKey
|
BasePathWithID = BasePath + "/:" + IDKey
|
||||||
ContextPath = BasePath + "/context"
|
|
||||||
RebloggedPath = BasePath + "/reblogged_by"
|
ContextPath = BasePathWithID + "/context"
|
||||||
FavouritedPath = BasePath + "/favourited_by"
|
|
||||||
FavouritePath = BasePath + "/favourite"
|
FavouritedPath = BasePathWithID + "/favourited_by"
|
||||||
ReblogPath = BasePath + "/reblog"
|
FavouritePath = BasePathWithID + "/favourite"
|
||||||
UnreblogPath = BasePath + "/unreblog"
|
UnfavouritePath = BasePathWithID + "/unfavourite"
|
||||||
BookmarkPath = BasePath + "/bookmark"
|
|
||||||
UnbookmarkPath = BasePath + "/unbookmark"
|
RebloggedPath = BasePathWithID + "/reblogged_by"
|
||||||
MutePath = BasePath + "/mute"
|
ReblogPath = BasePathWithID + "/reblog"
|
||||||
UnmutePath = BasePath + "/unmute"
|
UnreblogPath = BasePathWithID + "/unreblog"
|
||||||
PinPath = BasePath + "/pin"
|
|
||||||
UnpinPath = BasePath + "/unpin"
|
BookmarkPath = BasePathWithID + "/bookmark"
|
||||||
|
UnbookmarkPath = BasePathWithID + "/unbookmark"
|
||||||
|
|
||||||
|
MutePath = BasePathWithID + "/mute"
|
||||||
|
UnmutePath = BasePathWithID + "/unmute"
|
||||||
|
|
||||||
|
PinPath = BasePathWithID + "/pin"
|
||||||
|
UnpinPath = BasePathWithID + "/unpin"
|
||||||
)
|
)
|
||||||
|
|
||||||
type StatusModule struct {
|
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
|
// Route attaches all routes from this module to the given router
|
||||||
func (m *StatusModule) Route(r router.Router) error {
|
func (m *StatusModule) Route(r router.Router) error {
|
||||||
r.AttachHandler(http.MethodPost, BasePath, m.StatusCreatePOSTHandler)
|
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.MethodGet, BasePathWithID, m.muxHandler)
|
||||||
r.AttachHandler(http.MethodDelete, BasePathWithID, m.muxHandler)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -113,16 +125,15 @@ func (m *StatusModule) CreateTables(db db.DB) error {
|
||||||
func (m *StatusModule) muxHandler(c *gin.Context) {
|
func (m *StatusModule) muxHandler(c *gin.Context) {
|
||||||
m.log.Debug("entering mux handler")
|
m.log.Debug("entering mux handler")
|
||||||
ru := c.Request.RequestURI
|
ru := c.Request.RequestURI
|
||||||
if c.Request.Method == http.MethodGet {
|
|
||||||
|
switch c.Request.Method {
|
||||||
|
case http.MethodGet:
|
||||||
if strings.HasPrefix(ru, ContextPath) {
|
if strings.HasPrefix(ru, ContextPath) {
|
||||||
// TODO
|
// TODO
|
||||||
} else if strings.HasPrefix(ru, RebloggedPath) {
|
} else if strings.HasPrefix(ru, FavouritedPath) {
|
||||||
// TODO
|
m.StatusFavedByGETHandler(c)
|
||||||
} else {
|
} else {
|
||||||
m.StatusGETHandler(c)
|
m.StatusGETHandler(c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if c.Request.Method == http.MethodDelete {
|
|
||||||
m.StatusDELETEHandler(c)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
136
internal/apimodule/status/statusfave.go
Normal file
136
internal/apimodule/status/statusfave.go
Normal file
|
|
@ -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)
|
||||||
|
}
|
||||||
128
internal/apimodule/status/statusfavedby.go
Normal file
128
internal/apimodule/status/statusfavedby.go
Normal file
|
|
@ -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)
|
||||||
|
}
|
||||||
136
internal/apimodule/status/statusunfave.go
Normal file
136
internal/apimodule/status/statusunfave.go
Normal file
|
|
@ -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)
|
||||||
|
}
|
||||||
208
internal/apimodule/status/test/statusfave_test.go
Normal file
208
internal/apimodule/status/test/statusfave_test.go
Normal file
|
|
@ -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))
|
||||||
|
}
|
||||||
159
internal/apimodule/status/test/statusfavedby_test.go
Normal file
159
internal/apimodule/status/test/statusfavedby_test.go
Normal file
|
|
@ -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))
|
||||||
|
}
|
||||||
219
internal/apimodule/status/test/statusunfave_test.go
Normal file
219
internal/apimodule/status/test/statusunfave_test.go
Normal file
|
|
@ -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 checks if a given status has been pinned by a given account ID
|
||||||
StatusPinnedBy(status *gtsmodel.Status, accountID string) (bool, error)
|
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
|
USEFUL CONVERSION FUNCTIONS
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -44,26 +44,10 @@ type Account struct {
|
||||||
ACCOUNT METADATA
|
ACCOUNT METADATA
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// File name of the avatar on local storage
|
// ID of the avatar as a media attachment
|
||||||
AvatarFileName string
|
AvatarMediaAttachmentID string
|
||||||
// Gif? png? jpeg?
|
// ID of the header as a media attachment
|
||||||
AvatarContentType string
|
HeaderMediaAttachmentID 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
|
|
||||||
// DisplayName for this account. Can be empty, then just the Username will be used for display purposes.
|
// DisplayName for this account. Can be empty, then just the Username will be used for display purposes.
|
||||||
DisplayName string
|
DisplayName string
|
||||||
// a key/value map of fields that this account has added to their profile
|
// 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
|
// StatusFave refers to a 'fave' or 'like' in the database, from one account, targeting the status of another account
|
||||||
type StatusFave struct {
|
type StatusFave struct {
|
||||||
// id of this fave in the database
|
// 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
|
// 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
|
// id of the account that created ('did') the fave
|
||||||
AccountID string `pg:",notnull"`
|
AccountID string `pg:",notnull"`
|
||||||
// id the account owning the faved status
|
// id the account owning the faved status
|
||||||
TargetAccountID string `pg:",notnull"`
|
TargetAccountID string `pg:",notnull"`
|
||||||
// database id of the status that has been 'faved'
|
// 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 {
|
func (ps *postgresService) SetHeaderOrAvatarForAccountID(mediaAttachment *gtsmodel.MediaAttachment, accountID string) error {
|
||||||
_, err := ps.conn.Model(mediaAttachment).Insert()
|
if mediaAttachment.Avatar && mediaAttachment.Header {
|
||||||
return err
|
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 {
|
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 {
|
if err == pg.ErrNoRows {
|
||||||
return ErrNoEntries{}
|
return ErrNoEntries{}
|
||||||
}
|
}
|
||||||
|
|
@ -512,7 +544,19 @@ func (ps *postgresService) GetHeaderForAccountID(header *gtsmodel.MediaAttachmen
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ps *postgresService) GetAvatarForAccountID(avatar *gtsmodel.MediaAttachment, accountID string) error {
|
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 {
|
if err == pg.ErrNoRows {
|
||||||
return ErrNoEntries{}
|
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()
|
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
|
CONVERSION FUNCTIONS
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -170,8 +170,8 @@ func (c *converter) AccountToMastoPublic(a *gtsmodel.Account) (*mastotypes.Accou
|
||||||
return nil, fmt.Errorf("error getting avatar: %s", err)
|
return nil, fmt.Errorf("error getting avatar: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
aviURL := avi.File.Path
|
aviURL := avi.URL
|
||||||
aviURLStatic := avi.Thumbnail.Path
|
aviURLStatic := avi.Thumbnail.URL
|
||||||
|
|
||||||
header := >smodel.MediaAttachment{}
|
header := >smodel.MediaAttachment{}
|
||||||
if err := c.db.GetHeaderForAccountID(avi, a.ID); err != nil {
|
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)
|
return nil, fmt.Errorf("error getting header: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
headerURL := header.File.Path
|
headerURL := header.URL
|
||||||
headerURLStatic := header.Thumbnail.Path
|
headerURLStatic := header.Thumbnail.URL
|
||||||
|
|
||||||
// get the fields set on this account
|
// get the fields set on this account
|
||||||
fields := []mastotypes.Field{}
|
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 {
|
if err := db.CreateInstanceAccount(); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
BIN
testrig/media/zork-original.jpeg
Normal file
BIN
testrig/media/zork-original.jpeg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 447 KiB |
BIN
testrig/media/zork-small.jpeg
Normal file
BIN
testrig/media/zork-small.jpeg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
|
|
@ -41,6 +41,16 @@ func NewTestTokens() map[string]*oauth.Token {
|
||||||
AccessCreateAt: time.Now(),
|
AccessCreateAt: time.Now(),
|
||||||
AccessExpiresAt: time.Now().Add(72 * time.Hour),
|
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
|
return tokens
|
||||||
}
|
}
|
||||||
|
|
@ -243,184 +253,152 @@ func NewTestAccounts() map[string]*gtsmodel.Account {
|
||||||
Username: "localhost:8080",
|
Username: "localhost:8080",
|
||||||
},
|
},
|
||||||
"unconfirmed_account": {
|
"unconfirmed_account": {
|
||||||
ID: "59e197f5-87cd-4be8-ac7c-09082ccc4b4d",
|
ID: "59e197f5-87cd-4be8-ac7c-09082ccc4b4d",
|
||||||
Username: "weed_lord420",
|
Username: "weed_lord420",
|
||||||
AvatarFileName: "",
|
AvatarMediaAttachmentID: "",
|
||||||
AvatarContentType: "",
|
HeaderMediaAttachmentID: "",
|
||||||
AvatarFileSize: 0,
|
DisplayName: "",
|
||||||
AvatarUpdatedAt: time.Time{},
|
Fields: []gtsmodel.Field{},
|
||||||
AvatarRemoteURL: "",
|
Note: "",
|
||||||
HeaderFileName: "",
|
Memorial: false,
|
||||||
HeaderContentType: "",
|
MovedToAccountID: "",
|
||||||
HeaderFileSize: 0,
|
CreatedAt: time.Now(),
|
||||||
HeaderUpdatedAt: time.Time{},
|
UpdatedAt: time.Now(),
|
||||||
HeaderRemoteURL: "",
|
Bot: false,
|
||||||
DisplayName: "",
|
Reason: "hi, please let me in! I'm looking for somewhere neato bombeato to hang out.",
|
||||||
Fields: []gtsmodel.Field{},
|
Locked: false,
|
||||||
Note: "",
|
Discoverable: false,
|
||||||
Memorial: false,
|
Privacy: gtsmodel.VisibilityPublic,
|
||||||
MovedToAccountID: "",
|
Sensitive: false,
|
||||||
CreatedAt: time.Now(),
|
Language: "en",
|
||||||
UpdatedAt: time.Now(),
|
URI: "http://localhost:8080/users/weed_lord420",
|
||||||
Bot: false,
|
URL: "http://localhost:8080/@weed_lord420",
|
||||||
Reason: "hi, please let me in! I'm looking for somewhere neato bombeato to hang out.",
|
LastWebfingeredAt: time.Time{},
|
||||||
Locked: false,
|
InboxURL: "http://localhost:8080/users/weed_lord420/inbox",
|
||||||
Discoverable: false,
|
OutboxURL: "http://localhost:8080/users/weed_lord420/outbox",
|
||||||
Privacy: gtsmodel.VisibilityPublic,
|
SharedInboxURL: "",
|
||||||
Sensitive: false,
|
FollowersURL: "http://localhost:8080/users/weed_lord420/followers",
|
||||||
Language: "en",
|
FeaturedCollectionURL: "http://localhost:8080/users/weed_lord420/collections/featured",
|
||||||
URI: "http://localhost:8080/users/weed_lord420",
|
ActorType: gtsmodel.ActivityStreamsPerson,
|
||||||
URL: "http://localhost:8080/@weed_lord420",
|
AlsoKnownAs: "",
|
||||||
LastWebfingeredAt: time.Time{},
|
PrivateKey: &rsa.PrivateKey{},
|
||||||
InboxURL: "http://localhost:8080/users/weed_lord420/inbox",
|
PublicKey: &rsa.PublicKey{},
|
||||||
OutboxURL: "http://localhost:8080/users/weed_lord420/outbox",
|
SensitizedAt: time.Time{},
|
||||||
SharedInboxURL: "",
|
SilencedAt: time.Time{},
|
||||||
FollowersURL: "http://localhost:8080/users/weed_lord420/followers",
|
SuspendedAt: time.Time{},
|
||||||
FeaturedCollectionURL: "http://localhost:8080/users/weed_lord420/collections/featured",
|
HideCollections: false,
|
||||||
ActorType: gtsmodel.ActivityStreamsPerson,
|
SuspensionOrigin: "",
|
||||||
AlsoKnownAs: "",
|
|
||||||
PrivateKey: &rsa.PrivateKey{},
|
|
||||||
PublicKey: &rsa.PublicKey{},
|
|
||||||
SensitizedAt: time.Time{},
|
|
||||||
SilencedAt: time.Time{},
|
|
||||||
SuspendedAt: time.Time{},
|
|
||||||
HideCollections: false,
|
|
||||||
SuspensionOrigin: "",
|
|
||||||
},
|
},
|
||||||
"admin_account": {
|
"admin_account": {
|
||||||
ID: "8020dbb4-1e7b-4d99-a872-4cf94e64210f",
|
ID: "8020dbb4-1e7b-4d99-a872-4cf94e64210f",
|
||||||
Username: "admin",
|
Username: "admin",
|
||||||
AvatarFileName: "",
|
AvatarMediaAttachmentID: "",
|
||||||
AvatarContentType: "",
|
HeaderMediaAttachmentID: "",
|
||||||
AvatarFileSize: 0,
|
DisplayName: "",
|
||||||
AvatarUpdatedAt: time.Time{},
|
Fields: []gtsmodel.Field{},
|
||||||
AvatarRemoteURL: "",
|
Note: "",
|
||||||
HeaderFileName: "",
|
Memorial: false,
|
||||||
HeaderContentType: "",
|
MovedToAccountID: "",
|
||||||
HeaderFileSize: 0,
|
CreatedAt: time.Now().Add(-72 * time.Hour),
|
||||||
HeaderUpdatedAt: time.Time{},
|
UpdatedAt: time.Now().Add(-72 * time.Hour),
|
||||||
HeaderRemoteURL: "",
|
Bot: false,
|
||||||
DisplayName: "",
|
Reason: "",
|
||||||
Fields: []gtsmodel.Field{},
|
Locked: false,
|
||||||
Note: "",
|
Discoverable: true,
|
||||||
Memorial: false,
|
Privacy: gtsmodel.VisibilityPublic,
|
||||||
MovedToAccountID: "",
|
Sensitive: false,
|
||||||
CreatedAt: time.Now().Add(-72 * time.Hour),
|
Language: "en",
|
||||||
UpdatedAt: time.Now().Add(-72 * time.Hour),
|
URI: "http://localhost:8080/users/admin",
|
||||||
Bot: false,
|
URL: "http://localhost:8080/@admin",
|
||||||
Reason: "",
|
LastWebfingeredAt: time.Time{},
|
||||||
Locked: false,
|
InboxURL: "http://localhost:8080/users/admin/inbox",
|
||||||
Discoverable: true,
|
OutboxURL: "http://localhost:8080/users/admin/outbox",
|
||||||
Privacy: gtsmodel.VisibilityPublic,
|
SharedInboxURL: "",
|
||||||
Sensitive: false,
|
FollowersURL: "http://localhost:8080/users/admin/followers",
|
||||||
Language: "en",
|
FeaturedCollectionURL: "http://localhost:8080/users/admin/collections/featured",
|
||||||
URI: "http://localhost:8080/users/admin",
|
ActorType: gtsmodel.ActivityStreamsPerson,
|
||||||
URL: "http://localhost:8080/@admin",
|
AlsoKnownAs: "",
|
||||||
LastWebfingeredAt: time.Time{},
|
PrivateKey: &rsa.PrivateKey{},
|
||||||
InboxURL: "http://localhost:8080/users/admin/inbox",
|
PublicKey: &rsa.PublicKey{},
|
||||||
OutboxURL: "http://localhost:8080/users/admin/outbox",
|
SensitizedAt: time.Time{},
|
||||||
SharedInboxURL: "",
|
SilencedAt: time.Time{},
|
||||||
FollowersURL: "http://localhost:8080/users/admin/followers",
|
SuspendedAt: time.Time{},
|
||||||
FeaturedCollectionURL: "http://localhost:8080/users/admin/collections/featured",
|
HideCollections: false,
|
||||||
ActorType: gtsmodel.ActivityStreamsPerson,
|
SuspensionOrigin: "",
|
||||||
AlsoKnownAs: "",
|
|
||||||
PrivateKey: &rsa.PrivateKey{},
|
|
||||||
PublicKey: &rsa.PublicKey{},
|
|
||||||
SensitizedAt: time.Time{},
|
|
||||||
SilencedAt: time.Time{},
|
|
||||||
SuspendedAt: time.Time{},
|
|
||||||
HideCollections: false,
|
|
||||||
SuspensionOrigin: "",
|
|
||||||
},
|
},
|
||||||
"local_account_1": {
|
"local_account_1": {
|
||||||
ID: "580072df-4d03-4684-a412-89fd6f7d77e6",
|
ID: "580072df-4d03-4684-a412-89fd6f7d77e6",
|
||||||
Username: "the_mighty_zork",
|
Username: "the_mighty_zork",
|
||||||
AvatarFileName: "http://localhost:8080/fileserver/media/580072df-4d03-4684-a412-89fd6f7d77e6/avatar/original/75cfbe52-a5fb-451b-8f5a-b023229dce8d.jpeg",
|
AvatarMediaAttachmentID: "a849906f-8b8e-4b43-ac2f-6979ccbcd442",
|
||||||
AvatarContentType: "image/jpeg",
|
HeaderMediaAttachmentID: "",
|
||||||
AvatarFileSize: 0,
|
DisplayName: "original zork (he/they)",
|
||||||
AvatarUpdatedAt: time.Time{},
|
Fields: []gtsmodel.Field{},
|
||||||
AvatarRemoteURL: "",
|
Note: "hey yo this is my profile!",
|
||||||
HeaderFileName: "http://localhost:8080/fileserver/media/580072df-4d03-4684-a412-89fd6f7d77e6/header/original/9651c1ed-c288-4063-a95c-c7f8ff2a633f.jpeg",
|
Memorial: false,
|
||||||
HeaderContentType: "image/jpeg",
|
MovedToAccountID: "",
|
||||||
HeaderFileSize: 0,
|
CreatedAt: time.Now().Add(-48 * time.Hour),
|
||||||
HeaderUpdatedAt: time.Time{},
|
UpdatedAt: time.Now().Add(-48 * time.Hour),
|
||||||
HeaderRemoteURL: "",
|
Bot: false,
|
||||||
DisplayName: "original zork (he/they)",
|
Reason: "I wanna be on this damned webbed site so bad! Please! Wow",
|
||||||
Fields: []gtsmodel.Field{},
|
Locked: false,
|
||||||
Note: "hey yo this is my profile!",
|
Discoverable: true,
|
||||||
Memorial: false,
|
Privacy: gtsmodel.VisibilityPublic,
|
||||||
MovedToAccountID: "",
|
Sensitive: false,
|
||||||
CreatedAt: time.Now().Add(-48 * time.Hour),
|
Language: "en",
|
||||||
UpdatedAt: time.Now().Add(-48 * time.Hour),
|
URI: "http://localhost:8080/users/the_mighty_zork",
|
||||||
Bot: false,
|
URL: "http://localhost:8080/@the_mighty_zork",
|
||||||
Reason: "I wanna be on this damned webbed site so bad! Please! Wow",
|
LastWebfingeredAt: time.Time{},
|
||||||
Locked: false,
|
InboxURL: "http://localhost:8080/users/the_mighty_zork/inbox",
|
||||||
Discoverable: true,
|
OutboxURL: "http://localhost:8080/users/the_mighty_zork/outbox",
|
||||||
Privacy: gtsmodel.VisibilityPublic,
|
SharedInboxURL: "",
|
||||||
Sensitive: false,
|
FollowersURL: "http://localhost:8080/users/the_mighty_zork/followers",
|
||||||
Language: "en",
|
FeaturedCollectionURL: "http://localhost:8080/users/the_mighty_zork/collections/featured",
|
||||||
URI: "http://localhost:8080/users/the_mighty_zork",
|
ActorType: gtsmodel.ActivityStreamsPerson,
|
||||||
URL: "http://localhost:8080/@the_mighty_zork",
|
AlsoKnownAs: "",
|
||||||
LastWebfingeredAt: time.Time{},
|
PrivateKey: &rsa.PrivateKey{},
|
||||||
InboxURL: "http://localhost:8080/users/the_mighty_zork/inbox",
|
PublicKey: &rsa.PublicKey{},
|
||||||
OutboxURL: "http://localhost:8080/users/the_mighty_zork/outbox",
|
SensitizedAt: time.Time{},
|
||||||
SharedInboxURL: "",
|
SilencedAt: time.Time{},
|
||||||
FollowersURL: "http://localhost:8080/users/the_mighty_zork/followers",
|
SuspendedAt: time.Time{},
|
||||||
FeaturedCollectionURL: "http://localhost:8080/users/the_mighty_zork/collections/featured",
|
HideCollections: false,
|
||||||
ActorType: gtsmodel.ActivityStreamsPerson,
|
SuspensionOrigin: "",
|
||||||
AlsoKnownAs: "",
|
|
||||||
PrivateKey: &rsa.PrivateKey{},
|
|
||||||
PublicKey: &rsa.PublicKey{},
|
|
||||||
SensitizedAt: time.Time{},
|
|
||||||
SilencedAt: time.Time{},
|
|
||||||
SuspendedAt: time.Time{},
|
|
||||||
HideCollections: false,
|
|
||||||
SuspensionOrigin: "",
|
|
||||||
},
|
},
|
||||||
"local_account_2": {
|
"local_account_2": {
|
||||||
ID: "eecaad73-5703-426d-9312-276641daa31e",
|
ID: "eecaad73-5703-426d-9312-276641daa31e",
|
||||||
Username: "1happyturtle",
|
Username: "1happyturtle",
|
||||||
AvatarFileName: "http://localhost:8080/fileserver/media/eecaad73-5703-426d-9312-276641daa31e/avatar/original/d5e7c265-91a6-4d84-8c27-7e1efe5720da.jpeg",
|
AvatarMediaAttachmentID: "",
|
||||||
AvatarContentType: "image/jpeg",
|
HeaderMediaAttachmentID: "",
|
||||||
AvatarFileSize: 0,
|
DisplayName: "happy little turtle :3",
|
||||||
AvatarUpdatedAt: time.Time{},
|
Fields: []gtsmodel.Field{},
|
||||||
AvatarRemoteURL: "",
|
Note: "i post about things that concern me",
|
||||||
HeaderFileName: "http://localhost:8080/fileserver/media/eecaad73-5703-426d-9312-276641daa31e/header/original/e75d4117-21b6-4315-a428-eb3944235996.jpeg",
|
Memorial: false,
|
||||||
HeaderContentType: "image/jpeg",
|
MovedToAccountID: "",
|
||||||
HeaderFileSize: 0,
|
CreatedAt: time.Now().Add(-190 * time.Hour),
|
||||||
HeaderUpdatedAt: time.Time{},
|
UpdatedAt: time.Now().Add(-36 * time.Hour),
|
||||||
HeaderRemoteURL: "",
|
Bot: false,
|
||||||
DisplayName: "happy little turtle :3",
|
Reason: "",
|
||||||
Fields: []gtsmodel.Field{},
|
Locked: true,
|
||||||
Note: "i post about things that concern me",
|
Discoverable: false,
|
||||||
Memorial: false,
|
Privacy: gtsmodel.VisibilityFollowersOnly,
|
||||||
MovedToAccountID: "",
|
Sensitive: false,
|
||||||
CreatedAt: time.Now().Add(-190 * time.Hour),
|
Language: "en",
|
||||||
UpdatedAt: time.Now().Add(-36 * time.Hour),
|
URI: "http://localhost:8080/users/1happyturtle",
|
||||||
Bot: false,
|
URL: "http://localhost:8080/@1happyturtle",
|
||||||
Reason: "",
|
LastWebfingeredAt: time.Time{},
|
||||||
Locked: true,
|
InboxURL: "http://localhost:8080/users/1happyturtle/inbox",
|
||||||
Discoverable: false,
|
OutboxURL: "http://localhost:8080/users/1happyturtle/outbox",
|
||||||
Privacy: gtsmodel.VisibilityFollowersOnly,
|
SharedInboxURL: "",
|
||||||
Sensitive: false,
|
FollowersURL: "http://localhost:8080/users/1happyturtle/followers",
|
||||||
Language: "en",
|
FeaturedCollectionURL: "http://localhost:8080/users/1happyturtle/collections/featured",
|
||||||
URI: "http://localhost:8080/users/1happyturtle",
|
ActorType: gtsmodel.ActivityStreamsPerson,
|
||||||
URL: "http://localhost:8080/@1happyturtle",
|
AlsoKnownAs: "",
|
||||||
LastWebfingeredAt: time.Time{},
|
PrivateKey: &rsa.PrivateKey{},
|
||||||
InboxURL: "http://localhost:8080/users/1happyturtle/inbox",
|
PublicKey: &rsa.PublicKey{},
|
||||||
OutboxURL: "http://localhost:8080/users/1happyturtle/outbox",
|
SensitizedAt: time.Time{},
|
||||||
SharedInboxURL: "",
|
SilencedAt: time.Time{},
|
||||||
FollowersURL: "http://localhost:8080/users/1happyturtle/followers",
|
SuspendedAt: time.Time{},
|
||||||
FeaturedCollectionURL: "http://localhost:8080/users/1happyturtle/collections/featured",
|
HideCollections: false,
|
||||||
ActorType: gtsmodel.ActivityStreamsPerson,
|
SuspensionOrigin: "",
|
||||||
AlsoKnownAs: "",
|
|
||||||
PrivateKey: &rsa.PrivateKey{},
|
|
||||||
PublicKey: &rsa.PublicKey{},
|
|
||||||
SensitizedAt: time.Time{},
|
|
||||||
SilencedAt: time.Time{},
|
|
||||||
SuspendedAt: time.Time{},
|
|
||||||
HideCollections: false,
|
|
||||||
SuspensionOrigin: "",
|
|
||||||
},
|
},
|
||||||
"remote_account_1": {
|
"remote_account_1": {
|
||||||
ID: "c2c6e647-e2a9-4286-883b-e4a188186664",
|
ID: "c2c6e647-e2a9-4286-883b-e4a188186664",
|
||||||
|
|
@ -643,9 +621,58 @@ func NewTestAttachments() map[string]*gtsmodel.MediaAttachment {
|
||||||
Avatar: false,
|
Avatar: false,
|
||||||
Header: 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 {
|
func NewTestEmojis() map[string]*gtsmodel.Emoji {
|
||||||
return map[string]*gtsmodel.Emoji{
|
return map[string]*gtsmodel.Emoji{
|
||||||
"rainbow": {
|
"rainbow": {
|
||||||
|
|
@ -693,9 +720,14 @@ func NewTestStoredAttachments() map[string]filenames {
|
||||||
original: "ohyou-original.jpeg",
|
original: "ohyou-original.jpeg",
|
||||||
small: "ohyou-small.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 {
|
func NewTestStoredEmoji() map[string]filenames {
|
||||||
return map[string]filenames{
|
return map[string]filenames{
|
||||||
"rainbow": {
|
"rainbow": {
|
||||||
|
|
@ -710,24 +742,24 @@ func NewTestStoredEmoji() map[string]filenames {
|
||||||
func NewTestStatuses() map[string]*gtsmodel.Status {
|
func NewTestStatuses() map[string]*gtsmodel.Status {
|
||||||
return map[string]*gtsmodel.Status{
|
return map[string]*gtsmodel.Status{
|
||||||
"admin_account_status_1": {
|
"admin_account_status_1": {
|
||||||
ID: "502ccd6f-0edf-48d7-9016-2dfa4d3714cd",
|
ID: "502ccd6f-0edf-48d7-9016-2dfa4d3714cd",
|
||||||
URI: "http://localhost:8080/users/admin/statuses/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",
|
URL: "http://localhost:8080/@admin/statuses/502ccd6f-0edf-48d7-9016-2dfa4d3714cd",
|
||||||
Content: "hello world! #welcome ! first post on the instance :rainbow: !",
|
Content: "hello world! #welcome ! first post on the instance :rainbow: !",
|
||||||
Attachments: []string{"b052241b-f30f-4dc6-92fc-2bad0be1f8d8"},
|
Attachments: []string{"b052241b-f30f-4dc6-92fc-2bad0be1f8d8"},
|
||||||
Tags: []string{"a7e8f5ca-88a1-4652-8079-a187eab8d56e"},
|
Tags: []string{"a7e8f5ca-88a1-4652-8079-a187eab8d56e"},
|
||||||
Mentions: []string{},
|
Mentions: []string{},
|
||||||
Emojis: []string{"a96ec4f3-1cae-47e4-a508-f9d66a6b221b"},
|
Emojis: []string{"a96ec4f3-1cae-47e4-a508-f9d66a6b221b"},
|
||||||
CreatedAt: time.Now().Add(-71 * time.Hour),
|
CreatedAt: time.Now().Add(-71 * time.Hour),
|
||||||
UpdatedAt: time.Now().Add(-71 * time.Hour),
|
UpdatedAt: time.Now().Add(-71 * time.Hour),
|
||||||
Local: true,
|
Local: true,
|
||||||
AccountID: "8020dbb4-1e7b-4d99-a872-4cf94e64210f",
|
AccountID: "8020dbb4-1e7b-4d99-a872-4cf94e64210f",
|
||||||
InReplyToID: "",
|
InReplyToID: "",
|
||||||
BoostOfID: "",
|
BoostOfID: "",
|
||||||
ContentWarning: "",
|
ContentWarning: "",
|
||||||
Visibility: gtsmodel.VisibilityPublic,
|
Visibility: gtsmodel.VisibilityPublic,
|
||||||
Sensitive: false,
|
Sensitive: false,
|
||||||
Language: "en",
|
Language: "en",
|
||||||
CreatedWithApplicationID: "9bf9e368-037f-444d-8ffd-1091d1c21c4c",
|
CreatedWithApplicationID: "9bf9e368-037f-444d-8ffd-1091d1c21c4c",
|
||||||
VisibilityAdvanced: >smodel.VisibilityAdvanced{
|
VisibilityAdvanced: >smodel.VisibilityAdvanced{
|
||||||
Federated: true,
|
Federated: true,
|
||||||
|
|
@ -738,20 +770,20 @@ func NewTestStatuses() map[string]*gtsmodel.Status {
|
||||||
ActivityStreamsType: gtsmodel.ActivityStreamsNote,
|
ActivityStreamsType: gtsmodel.ActivityStreamsNote,
|
||||||
},
|
},
|
||||||
"admin_account_status_2": {
|
"admin_account_status_2": {
|
||||||
ID: "0fb3f1ac-5cd8-48ac-9050-3d95dc7e44e9",
|
ID: "0fb3f1ac-5cd8-48ac-9050-3d95dc7e44e9",
|
||||||
URI: "http://localhost:8080/users/admin/statuses/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",
|
URL: "http://localhost:8080/@admin/statuses/0fb3f1ac-5cd8-48ac-9050-3d95dc7e44e9",
|
||||||
Content: "🐕🐕🐕🐕🐕",
|
Content: "🐕🐕🐕🐕🐕",
|
||||||
CreatedAt: time.Now().Add(-70 * time.Hour),
|
CreatedAt: time.Now().Add(-70 * time.Hour),
|
||||||
UpdatedAt: time.Now().Add(-70 * time.Hour),
|
UpdatedAt: time.Now().Add(-70 * time.Hour),
|
||||||
Local: true,
|
Local: true,
|
||||||
AccountID: "8020dbb4-1e7b-4d99-a872-4cf94e64210f",
|
AccountID: "8020dbb4-1e7b-4d99-a872-4cf94e64210f",
|
||||||
InReplyToID: "",
|
InReplyToID: "",
|
||||||
BoostOfID: "",
|
BoostOfID: "",
|
||||||
ContentWarning: "open to see some puppies",
|
ContentWarning: "open to see some puppies",
|
||||||
Visibility: gtsmodel.VisibilityPublic,
|
Visibility: gtsmodel.VisibilityPublic,
|
||||||
Sensitive: true,
|
Sensitive: true,
|
||||||
Language: "en",
|
Language: "en",
|
||||||
CreatedWithApplicationID: "9bf9e368-037f-444d-8ffd-1091d1c21c4c",
|
CreatedWithApplicationID: "9bf9e368-037f-444d-8ffd-1091d1c21c4c",
|
||||||
VisibilityAdvanced: >smodel.VisibilityAdvanced{
|
VisibilityAdvanced: >smodel.VisibilityAdvanced{
|
||||||
Federated: true,
|
Federated: true,
|
||||||
|
|
@ -762,20 +794,20 @@ func NewTestStatuses() map[string]*gtsmodel.Status {
|
||||||
ActivityStreamsType: gtsmodel.ActivityStreamsNote,
|
ActivityStreamsType: gtsmodel.ActivityStreamsNote,
|
||||||
},
|
},
|
||||||
"local_account_1_status_1": {
|
"local_account_1_status_1": {
|
||||||
ID: "91b1e795-74ff-4672-a4c4-476616710e2d",
|
ID: "91b1e795-74ff-4672-a4c4-476616710e2d",
|
||||||
URI: "http://localhost:8080/users/the_mighty_zork/statuses/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",
|
URL: "http://localhost:8080/@the_mighty_zork/statuses/91b1e795-74ff-4672-a4c4-476616710e2d",
|
||||||
Content: "hello everyone!",
|
Content: "hello everyone!",
|
||||||
CreatedAt: time.Now().Add(-47 * time.Hour),
|
CreatedAt: time.Now().Add(-47 * time.Hour),
|
||||||
UpdatedAt: time.Now().Add(-47 * time.Hour),
|
UpdatedAt: time.Now().Add(-47 * time.Hour),
|
||||||
Local: true,
|
Local: true,
|
||||||
AccountID: "580072df-4d03-4684-a412-89fd6f7d77e6",
|
AccountID: "580072df-4d03-4684-a412-89fd6f7d77e6",
|
||||||
InReplyToID: "",
|
InReplyToID: "",
|
||||||
BoostOfID: "",
|
BoostOfID: "",
|
||||||
ContentWarning: "introduction post",
|
ContentWarning: "introduction post",
|
||||||
Visibility: gtsmodel.VisibilityPublic,
|
Visibility: gtsmodel.VisibilityPublic,
|
||||||
Sensitive: true,
|
Sensitive: true,
|
||||||
Language: "en",
|
Language: "en",
|
||||||
CreatedWithApplicationID: "f88697b8-ee3d-46c2-ac3f-dbb85566c3cc",
|
CreatedWithApplicationID: "f88697b8-ee3d-46c2-ac3f-dbb85566c3cc",
|
||||||
VisibilityAdvanced: >smodel.VisibilityAdvanced{
|
VisibilityAdvanced: >smodel.VisibilityAdvanced{
|
||||||
Federated: true,
|
Federated: true,
|
||||||
|
|
@ -786,20 +818,20 @@ func NewTestStatuses() map[string]*gtsmodel.Status {
|
||||||
ActivityStreamsType: gtsmodel.ActivityStreamsNote,
|
ActivityStreamsType: gtsmodel.ActivityStreamsNote,
|
||||||
},
|
},
|
||||||
"local_account_1_status_2": {
|
"local_account_1_status_2": {
|
||||||
ID: "3dd328d9-8bb1-48f5-bc96-5ccc1c696b4c",
|
ID: "3dd328d9-8bb1-48f5-bc96-5ccc1c696b4c",
|
||||||
URI: "http://localhost:8080/users/the_mighty_zork/statuses/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",
|
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",
|
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),
|
CreatedAt: time.Now().Add(-46 * time.Hour),
|
||||||
UpdatedAt: time.Now().Add(-46 * time.Hour),
|
UpdatedAt: time.Now().Add(-46 * time.Hour),
|
||||||
Local: true,
|
Local: true,
|
||||||
AccountID: "580072df-4d03-4684-a412-89fd6f7d77e6",
|
AccountID: "580072df-4d03-4684-a412-89fd6f7d77e6",
|
||||||
InReplyToID: "",
|
InReplyToID: "",
|
||||||
BoostOfID: "",
|
BoostOfID: "",
|
||||||
ContentWarning: "",
|
ContentWarning: "",
|
||||||
Visibility: gtsmodel.VisibilityUnlocked,
|
Visibility: gtsmodel.VisibilityUnlocked,
|
||||||
Sensitive: false,
|
Sensitive: false,
|
||||||
Language: "en",
|
Language: "en",
|
||||||
CreatedWithApplicationID: "f88697b8-ee3d-46c2-ac3f-dbb85566c3cc",
|
CreatedWithApplicationID: "f88697b8-ee3d-46c2-ac3f-dbb85566c3cc",
|
||||||
VisibilityAdvanced: >smodel.VisibilityAdvanced{
|
VisibilityAdvanced: >smodel.VisibilityAdvanced{
|
||||||
Federated: false,
|
Federated: false,
|
||||||
|
|
@ -810,20 +842,20 @@ func NewTestStatuses() map[string]*gtsmodel.Status {
|
||||||
ActivityStreamsType: gtsmodel.ActivityStreamsNote,
|
ActivityStreamsType: gtsmodel.ActivityStreamsNote,
|
||||||
},
|
},
|
||||||
"local_account_1_status_3": {
|
"local_account_1_status_3": {
|
||||||
ID: "5e41963f-8ab9-4147-9f00-52d56e19da65",
|
ID: "5e41963f-8ab9-4147-9f00-52d56e19da65",
|
||||||
URI: "http://localhost:8080/users/the_mighty_zork/statuses/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",
|
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",
|
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),
|
CreatedAt: time.Now().Add(-45 * time.Hour),
|
||||||
UpdatedAt: time.Now().Add(-45 * time.Hour),
|
UpdatedAt: time.Now().Add(-45 * time.Hour),
|
||||||
Local: true,
|
Local: true,
|
||||||
AccountID: "580072df-4d03-4684-a412-89fd6f7d77e6",
|
AccountID: "580072df-4d03-4684-a412-89fd6f7d77e6",
|
||||||
InReplyToID: "",
|
InReplyToID: "",
|
||||||
BoostOfID: "",
|
BoostOfID: "",
|
||||||
ContentWarning: "test: you shouldn't be able to interact with this post in any way",
|
ContentWarning: "test: you shouldn't be able to interact with this post in any way",
|
||||||
Visibility: gtsmodel.VisibilityMutualsOnly,
|
Visibility: gtsmodel.VisibilityMutualsOnly,
|
||||||
Sensitive: false,
|
Sensitive: false,
|
||||||
Language: "en",
|
Language: "en",
|
||||||
CreatedWithApplicationID: "f88697b8-ee3d-46c2-ac3f-dbb85566c3cc",
|
CreatedWithApplicationID: "f88697b8-ee3d-46c2-ac3f-dbb85566c3cc",
|
||||||
VisibilityAdvanced: >smodel.VisibilityAdvanced{
|
VisibilityAdvanced: >smodel.VisibilityAdvanced{
|
||||||
Federated: true,
|
Federated: true,
|
||||||
|
|
@ -834,21 +866,21 @@ func NewTestStatuses() map[string]*gtsmodel.Status {
|
||||||
ActivityStreamsType: gtsmodel.ActivityStreamsNote,
|
ActivityStreamsType: gtsmodel.ActivityStreamsNote,
|
||||||
},
|
},
|
||||||
"local_account_1_status_4": {
|
"local_account_1_status_4": {
|
||||||
ID: "18524c05-97dc-46d7-b474-c811bd9e1e32",
|
ID: "18524c05-97dc-46d7-b474-c811bd9e1e32",
|
||||||
URI: "http://localhost:8080/users/the_mighty_zork/statuses/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",
|
URL: "http://localhost:8080/@the_mighty_zork/statuses/18524c05-97dc-46d7-b474-c811bd9e1e32",
|
||||||
Content: "here's a little gif of trent",
|
Content: "here's a little gif of trent",
|
||||||
Attachments: []string{"510f6033-798b-4390-81b1-c38ca2205ad3"},
|
Attachments: []string{"510f6033-798b-4390-81b1-c38ca2205ad3"},
|
||||||
CreatedAt: time.Now().Add(-1 * time.Hour),
|
CreatedAt: time.Now().Add(-1 * time.Hour),
|
||||||
UpdatedAt: time.Now().Add(-1 * time.Hour),
|
UpdatedAt: time.Now().Add(-1 * time.Hour),
|
||||||
Local: true,
|
Local: true,
|
||||||
AccountID: "580072df-4d03-4684-a412-89fd6f7d77e6",
|
AccountID: "580072df-4d03-4684-a412-89fd6f7d77e6",
|
||||||
InReplyToID: "",
|
InReplyToID: "",
|
||||||
BoostOfID: "",
|
BoostOfID: "",
|
||||||
ContentWarning: "eye contact, trent reznor gif",
|
ContentWarning: "eye contact, trent reznor gif",
|
||||||
Visibility: gtsmodel.VisibilityMutualsOnly,
|
Visibility: gtsmodel.VisibilityMutualsOnly,
|
||||||
Sensitive: false,
|
Sensitive: false,
|
||||||
Language: "en",
|
Language: "en",
|
||||||
CreatedWithApplicationID: "f88697b8-ee3d-46c2-ac3f-dbb85566c3cc",
|
CreatedWithApplicationID: "f88697b8-ee3d-46c2-ac3f-dbb85566c3cc",
|
||||||
VisibilityAdvanced: >smodel.VisibilityAdvanced{
|
VisibilityAdvanced: >smodel.VisibilityAdvanced{
|
||||||
Federated: true,
|
Federated: true,
|
||||||
|
|
@ -859,20 +891,20 @@ func NewTestStatuses() map[string]*gtsmodel.Status {
|
||||||
ActivityStreamsType: gtsmodel.ActivityStreamsNote,
|
ActivityStreamsType: gtsmodel.ActivityStreamsNote,
|
||||||
},
|
},
|
||||||
"local_account_2_status_1": {
|
"local_account_2_status_1": {
|
||||||
ID: "8945ccf2-3873-45e9-aa13-fd7163f19775",
|
ID: "8945ccf2-3873-45e9-aa13-fd7163f19775",
|
||||||
URI: "http://localhost:8080/users/1happyturtle/statuses/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",
|
URL: "http://localhost:8080/@1happyturtle/statuses/8945ccf2-3873-45e9-aa13-fd7163f19775",
|
||||||
Content: "🐢 hi everyone i post about turtles 🐢",
|
Content: "🐢 hi everyone i post about turtles 🐢",
|
||||||
CreatedAt: time.Now().Add(-189 * time.Hour),
|
CreatedAt: time.Now().Add(-189 * time.Hour),
|
||||||
UpdatedAt: time.Now().Add(-189 * time.Hour),
|
UpdatedAt: time.Now().Add(-189 * time.Hour),
|
||||||
Local: true,
|
Local: true,
|
||||||
AccountID: "eecaad73-5703-426d-9312-276641daa31e",
|
AccountID: "eecaad73-5703-426d-9312-276641daa31e",
|
||||||
InReplyToID: "",
|
InReplyToID: "",
|
||||||
BoostOfID: "",
|
BoostOfID: "",
|
||||||
ContentWarning: "introduction post",
|
ContentWarning: "introduction post",
|
||||||
Visibility: gtsmodel.VisibilityPublic,
|
Visibility: gtsmodel.VisibilityPublic,
|
||||||
Sensitive: true,
|
Sensitive: true,
|
||||||
Language: "en",
|
Language: "en",
|
||||||
CreatedWithApplicationID: "6b0cd164-8497-4cd5-bec9-957886fac5df",
|
CreatedWithApplicationID: "6b0cd164-8497-4cd5-bec9-957886fac5df",
|
||||||
VisibilityAdvanced: >smodel.VisibilityAdvanced{
|
VisibilityAdvanced: >smodel.VisibilityAdvanced{
|
||||||
Federated: true,
|
Federated: true,
|
||||||
|
|
@ -883,20 +915,20 @@ func NewTestStatuses() map[string]*gtsmodel.Status {
|
||||||
ActivityStreamsType: gtsmodel.ActivityStreamsNote,
|
ActivityStreamsType: gtsmodel.ActivityStreamsNote,
|
||||||
},
|
},
|
||||||
"local_account_2_status_2": {
|
"local_account_2_status_2": {
|
||||||
ID: "c7e25a86-f0d3-4705-a73c-c597f687d3dd",
|
ID: "c7e25a86-f0d3-4705-a73c-c597f687d3dd",
|
||||||
URI: "http://localhost:8080/users/1happyturtle/statuses/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",
|
URL: "http://localhost:8080/@1happyturtle/statuses/c7e25a86-f0d3-4705-a73c-c597f687d3dd",
|
||||||
Content: "🐢 this one is federated, likeable, and boostable but not replyable 🐢",
|
Content: "🐢 this one is federated, likeable, and boostable but not replyable 🐢",
|
||||||
CreatedAt: time.Now().Add(-1 * time.Minute),
|
CreatedAt: time.Now().Add(-1 * time.Minute),
|
||||||
UpdatedAt: time.Now().Add(-1 * time.Minute),
|
UpdatedAt: time.Now().Add(-1 * time.Minute),
|
||||||
Local: true,
|
Local: true,
|
||||||
AccountID: "eecaad73-5703-426d-9312-276641daa31e",
|
AccountID: "eecaad73-5703-426d-9312-276641daa31e",
|
||||||
InReplyToID: "",
|
InReplyToID: "",
|
||||||
BoostOfID: "",
|
BoostOfID: "",
|
||||||
ContentWarning: "",
|
ContentWarning: "",
|
||||||
Visibility: gtsmodel.VisibilityPublic,
|
Visibility: gtsmodel.VisibilityPublic,
|
||||||
Sensitive: true,
|
Sensitive: true,
|
||||||
Language: "en",
|
Language: "en",
|
||||||
CreatedWithApplicationID: "6b0cd164-8497-4cd5-bec9-957886fac5df",
|
CreatedWithApplicationID: "6b0cd164-8497-4cd5-bec9-957886fac5df",
|
||||||
VisibilityAdvanced: >smodel.VisibilityAdvanced{
|
VisibilityAdvanced: >smodel.VisibilityAdvanced{
|
||||||
Federated: true,
|
Federated: true,
|
||||||
|
|
@ -906,9 +938,34 @@ func NewTestStatuses() map[string]*gtsmodel.Status {
|
||||||
},
|
},
|
||||||
ActivityStreamsType: gtsmodel.ActivityStreamsNote,
|
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 {
|
func NewTestTags() map[string]*gtsmodel.Tag {
|
||||||
return map[string]*gtsmodel.Tag{
|
return map[string]*gtsmodel.Tag{
|
||||||
"welcome": {
|
"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…
Add table
Add a link
Reference in a new issue