fixin' bugs and takin' names

This commit is contained in:
tsmethurst 2021-04-06 23:35:49 +02:00
parent 9eb8878e94
commit 1bf56e0a52
3 changed files with 138 additions and 60 deletions

View File

@ -27,6 +27,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/google/uuid"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/db/model"
"github.com/superseriousbusiness/gotosocial/internal/distributor"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
@ -36,12 +37,12 @@ import (
type advancedStatusCreateForm struct {
mastotypes.StatusCreateRequest
AdvancedVisibility *advancedVisibilityFlagsForm `form:"visibility_advanced"`
advancedVisibilityFlagsForm
}
type advancedVisibilityFlagsForm struct {
// The gotosocial visibility model
Visibility *model.Visibility
VisibilityAdvanced *model.Visibility `form:"visibility_advanced"`
// This status will be federated beyond the local timeline(s)
Federated *bool `form:"federated"`
// This status can be boosted/reblogged
@ -70,7 +71,7 @@ func (m *statusModule) statusCreatePOSTHandler(c *gin.Context) {
}
// extract the status create form from the request context
l.Trace("parsing request form")
l.Tracef("parsing request form: %s", c.Request.Form)
form := &advancedStatusCreateForm{}
if err := c.ShouldBind(form); err != nil || form == nil {
l.Debugf("could not parse form from request: %s", err)
@ -128,6 +129,12 @@ func (m *statusModule) statusCreatePOSTHandler(c *gin.Context) {
return
}
// handle language settings
if err := parseLanguage(form, authed.Account.Language, newStatus); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
// convert mentions to *model.Mention
menchies, err := m.db.MentionStringsToMentions(util.DeriveMentions(form.Status), authed.Account.ID, thisStatusID)
if err != nil {
@ -186,8 +193,9 @@ func (m *statusModule) statusCreatePOSTHandler(c *gin.Context) {
URI: newStatus.URI,
URL: newStatus.URL,
Content: newStatus.Content,
Application: authed.Application.ToMasto(),
Application: authed.Application.ToMastoPublic(),
Account: mastoAccount,
// MediaAttachments: ,
Text: form.Status,
}
c.JSON(http.StatusOK, mastoStatus)
@ -260,8 +268,8 @@ func parseVisibility(form *advancedStatusCreateForm, accountDefaultVis model.Vis
// Advanced takes priority if it's set.
// If it's not set, take whatever masto visibility is set.
// If *that's* not set either, then just take the account default.
if form.AdvancedVisibility != nil && form.AdvancedVisibility.Visibility != nil {
gtsBasicVis = *form.AdvancedVisibility.Visibility
if form.VisibilityAdvanced != nil {
gtsBasicVis = *form.VisibilityAdvanced
} else if form.Visibility != "" {
gtsBasicVis = util.ParseGTSVisFromMastoVis(form.Visibility)
} else {
@ -274,40 +282,38 @@ func parseVisibility(form *advancedStatusCreateForm, accountDefaultVis model.Vis
break
case model.VisibilityUnlocked:
// for unlocked the user can set any combination of flags they like so look at them all to see if they're set and then apply them
if form.AdvancedVisibility != nil {
if form.AdvancedVisibility.Federated != nil {
gtsAdvancedVis.Federated = *form.AdvancedVisibility.Federated
}
if form.AdvancedVisibility.Boostable != nil {
gtsAdvancedVis.Boostable = *form.AdvancedVisibility.Boostable
}
if form.AdvancedVisibility.Replyable != nil {
gtsAdvancedVis.Replyable = *form.AdvancedVisibility.Replyable
}
if form.AdvancedVisibility.Likeable != nil {
gtsAdvancedVis.Likeable = *form.AdvancedVisibility.Likeable
}
if form.Federated != nil {
gtsAdvancedVis.Federated = *form.Federated
}
if form.Boostable != nil {
gtsAdvancedVis.Boostable = *form.Boostable
}
if form.Replyable != nil {
gtsAdvancedVis.Replyable = *form.Replyable
}
if form.Likeable != nil {
gtsAdvancedVis.Likeable = *form.Likeable
}
case model.VisibilityFollowersOnly, model.VisibilityMutualsOnly:
// for followers or mutuals only, boostable will *always* be false, but the other fields can be set so check and apply them
gtsAdvancedVis.Boostable = false
if form.AdvancedVisibility != nil {
if form.AdvancedVisibility.Federated != nil {
gtsAdvancedVis.Federated = *form.AdvancedVisibility.Federated
}
if form.AdvancedVisibility.Replyable != nil {
gtsAdvancedVis.Replyable = *form.AdvancedVisibility.Replyable
}
if form.AdvancedVisibility.Likeable != nil {
gtsAdvancedVis.Likeable = *form.AdvancedVisibility.Likeable
}
if form.Federated != nil {
gtsAdvancedVis.Federated = *form.Federated
}
if form.Replyable != nil {
gtsAdvancedVis.Replyable = *form.Replyable
}
if form.Likeable != nil {
gtsAdvancedVis.Likeable = *form.Likeable
}
case model.VisibilityDirect:
// direct is pretty easy: there's only one possible setting so return it
gtsAdvancedVis.Federated = true
@ -336,9 +342,18 @@ func (m *statusModule) parseReplyToID(form *advancedStatusCreateForm, thisAccoun
repliedStatus := &model.Status{}
repliedAccount := &model.Account{}
// check replied status exists + is replyable
if err := m.db.GetByID(form.InReplyToID, repliedStatus); err != nil || !repliedStatus.VisibilityAdvanced.Replyable {
return fmt.Errorf("status with id %s not replyable: %s", form.InReplyToID, err)
if err := m.db.GetByID(form.InReplyToID, repliedStatus); err != nil {
if _, ok := err.(db.ErrNoEntries); ok {
return fmt.Errorf("status with id %s not replyable because it doesn't exist", form.InReplyToID)
} else {
return fmt.Errorf("status with id %s not replyable: %s", form.InReplyToID, err)
}
}
if !repliedStatus.VisibilityAdvanced.Replyable {
return fmt.Errorf("status with id %s is marked as not replyable", form.InReplyToID)
}
// check replied account is known to us
if err := m.db.GetByID(repliedStatus.AccountID, repliedAccount); err != nil {
return fmt.Errorf("status with id %s not replyable: %s", form.InReplyToID, err)
@ -373,3 +388,15 @@ func (m *statusModule) parseMediaIDs(form *advancedStatusCreateForm, thisAccount
status.Attachments = attachments
return nil
}
func parseLanguage(form *advancedStatusCreateForm, accountDefaultLanguage string, status *model.Status) error {
if form.Language != "" {
status.Language = form.Language
} else {
status.Language = accountDefaultLanguage
}
if status.Language == "" {
return errors.New("no language given either in status create form or account default")
}
return nil
}

View File

@ -165,27 +165,13 @@ func (suite *StatusCreateTestSuite) TestStatusCreatePOSTHandlerSuccessful() {
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"])
ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080/%s", basePath), nil) // the endpoint we're hitting
ctx.Request.Form = url.Values{
"status": {"this is a brand new status!"},
"spoiler_text": {"hello hello"},
"sensitive": {"true"},
"visibility": {"public"},
// Status string `form:"status"`
// // Array of Attachment ids to be attached as media. If provided, status becomes optional, and poll cannot be used.
// MediaIDs []string `form:"media_ids"`
// // Poll to include with this status.
// Poll *PollRequest `form:"poll"`
// // ID of the status being replied to, if status is a reply
// InReplyToID string `form:"in_reply_to_id"`
// // Mark status and attached media as sensitive?
// Sensitive bool `form:"sensitive"`
// // Text to be shown as a warning or subject before the actual content. Statuses are generally collapsed behind this field.
// SpoilerText string `form:"spoiler_text"`
// // Visibility of the posted status. Enumerable oneOf public, unlisted, private, direct.
// Visibility Visibility `form:"visibility"`
// // ISO 8601 Datetime at which to schedule a status. Providing this paramter will cause ScheduledStatus to be returned instead of Status. Must be at least 5 minutes in the future.
// ScheduledAt string `form:"scheduled_at"`
// // ISO 639 language code for this status.
// Language string `form:"language"`
"status": {"this is a brand new status!"},
"spoiler_text": {"hello hello"},
"sensitive": {"true"},
"visibility_advanced": {"mutuals_only"},
"likeable": {"false"},
"replyable": {"false"},
"federated": {"false"},
}
suite.statusModule.statusCreatePOSTHandler(ctx)
@ -198,6 +184,7 @@ func (suite *StatusCreateTestSuite) TestStatusCreatePOSTHandlerSuccessful() {
defer result.Body.Close()
b, err := ioutil.ReadAll(result.Body)
assert.NoError(suite.T(), err)
fmt.Println(string(b))
statusReply := &mastotypes.Status{}
err = json.Unmarshal(b, statusReply)
@ -206,7 +193,47 @@ func (suite *StatusCreateTestSuite) TestStatusCreatePOSTHandlerSuccessful() {
assert.Equal(suite.T(), "hello hello", statusReply.SpoilerText)
assert.Equal(suite.T(), "this is a brand new status!", statusReply.Content)
assert.True(suite.T(), statusReply.Sensitive)
assert.Equal(suite.T(), mastotypes.VisibilityPublic, statusReply.Visibility)
assert.Equal(suite.T(), mastotypes.VisibilityPrivate, statusReply.Visibility)
}
func (suite *StatusCreateTestSuite) TestStatusCreatePOSTHandlerReplyToFail() {
t := suite.testTokens["local_account_1"]
oauthToken := oauth.PGTokenToOauthToken(t)
// 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", basePath), nil) // the endpoint we're hitting
ctx.Request.Form = url.Values{
"status": {"this is a reply to a status that doesn't exist"},
"spoiler_text": {"don't open cuz it won't work"},
"in_reply_to_id": {"3759e7ef-8ee1-4c0c-86f6-8b70b9ad3d50"},
}
suite.statusModule.statusCreatePOSTHandler(ctx)
// check response
// 1. we should have OK from our call to the function
suite.EqualValues(http.StatusOK, recorder.Code)
result := recorder.Result()
defer result.Body.Close()
b, err := ioutil.ReadAll(result.Body)
assert.NoError(suite.T(), err)
fmt.Println(string(b))
statusReply := &mastotypes.Status{}
err = json.Unmarshal(b, statusReply)
assert.NoError(suite.T(), err)
assert.Equal(suite.T(), "hello hello", statusReply.SpoilerText)
assert.Equal(suite.T(), "this is a brand new status!", statusReply.Content)
assert.True(suite.T(), statusReply.Sensitive)
assert.Equal(suite.T(), mastotypes.VisibilityPrivate, statusReply.Visibility)
}
func TestStatusCreateTestSuite(t *testing.T) {

View File

@ -63,12 +63,36 @@ func GenerateURIs(username string, protocol string, host string) *URIs {
}
}
// ParseGTSVisFromMastoVis converts a mastodon visibility into its gts equivalent.
func ParseGTSVisFromMastoVis(m mastotypes.Visibility) model.Visibility {
// TODO: convert a masto vis into a gts vis
switch m {
case mastotypes.VisibilityPublic:
return model.VisibilityPublic
case mastotypes.VisibilityUnlisted:
return model.VisibilityUnlocked
case mastotypes.VisibilityPrivate:
return model.VisibilityFollowersOnly
case mastotypes.VisibilityDirect:
return model.VisibilityDirect
default:
break
}
return ""
}
// ParseMastoVisFromGTSVis converts a gts visibility into its mastodon equivalent
func ParseMastoVisFromGTSVis(m model.Visibility) mastotypes.Visibility {
// TODO: convert a gts vis into a masto vis
switch m {
case model.VisibilityPublic:
return mastotypes.VisibilityPublic
case model.VisibilityUnlocked:
return mastotypes.VisibilityUnlisted
case model.VisibilityFollowersOnly, model.VisibilityMutualsOnly:
return mastotypes.VisibilityPrivate
case model.VisibilityDirect:
return mastotypes.VisibilityDirect
default:
break
}
return ""
}