mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-10-31 01:02:25 -05:00 
			
		
		
		
	fixin' bugs and takin' names
This commit is contained in:
		
					parent
					
						
							
								9eb8878e94
							
						
					
				
			
			
				commit
				
					
						1bf56e0a52
					
				
			
		
					 3 changed files with 138 additions and 60 deletions
				
			
		|  | @ -27,6 +27,7 @@ import ( | ||||||
| 	"github.com/gin-gonic/gin" | 	"github.com/gin-gonic/gin" | ||||||
| 	"github.com/google/uuid" | 	"github.com/google/uuid" | ||||||
| 	"github.com/superseriousbusiness/gotosocial/internal/config" | 	"github.com/superseriousbusiness/gotosocial/internal/config" | ||||||
|  | 	"github.com/superseriousbusiness/gotosocial/internal/db" | ||||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db/model" | 	"github.com/superseriousbusiness/gotosocial/internal/db/model" | ||||||
| 	"github.com/superseriousbusiness/gotosocial/internal/distributor" | 	"github.com/superseriousbusiness/gotosocial/internal/distributor" | ||||||
| 	"github.com/superseriousbusiness/gotosocial/internal/oauth" | 	"github.com/superseriousbusiness/gotosocial/internal/oauth" | ||||||
|  | @ -36,12 +37,12 @@ import ( | ||||||
| 
 | 
 | ||||||
| type advancedStatusCreateForm struct { | type advancedStatusCreateForm struct { | ||||||
| 	mastotypes.StatusCreateRequest | 	mastotypes.StatusCreateRequest | ||||||
| 	AdvancedVisibility *advancedVisibilityFlagsForm `form:"visibility_advanced"` | 	advancedVisibilityFlagsForm | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type advancedVisibilityFlagsForm struct { | type advancedVisibilityFlagsForm struct { | ||||||
| 	// The gotosocial visibility model | 	// The gotosocial visibility model | ||||||
| 	Visibility *model.Visibility | 	VisibilityAdvanced *model.Visibility `form:"visibility_advanced"` | ||||||
| 	// This status will be federated beyond the local timeline(s) | 	// This status will be federated beyond the local timeline(s) | ||||||
| 	Federated *bool `form:"federated"` | 	Federated *bool `form:"federated"` | ||||||
| 	// This status can be boosted/reblogged | 	// 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 | 	// 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{} | 	form := &advancedStatusCreateForm{} | ||||||
| 	if err := c.ShouldBind(form); err != nil || form == nil { | 	if err := c.ShouldBind(form); err != nil || form == nil { | ||||||
| 		l.Debugf("could not parse form from request: %s", err) | 		l.Debugf("could not parse form from request: %s", err) | ||||||
|  | @ -128,6 +129,12 @@ func (m *statusModule) statusCreatePOSTHandler(c *gin.Context) { | ||||||
| 		return | 		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 | 	// convert mentions to *model.Mention | ||||||
| 	menchies, err := m.db.MentionStringsToMentions(util.DeriveMentions(form.Status), authed.Account.ID, thisStatusID) | 	menchies, err := m.db.MentionStringsToMentions(util.DeriveMentions(form.Status), authed.Account.ID, thisStatusID) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
|  | @ -186,8 +193,9 @@ func (m *statusModule) statusCreatePOSTHandler(c *gin.Context) { | ||||||
| 		URI:         newStatus.URI, | 		URI:         newStatus.URI, | ||||||
| 		URL:         newStatus.URL, | 		URL:         newStatus.URL, | ||||||
| 		Content:     newStatus.Content, | 		Content:     newStatus.Content, | ||||||
| 		Application: authed.Application.ToMasto(), | 		Application: authed.Application.ToMastoPublic(), | ||||||
| 		Account:     mastoAccount, | 		Account:     mastoAccount, | ||||||
|  | 		// MediaAttachments: , | ||||||
| 		Text:        form.Status, | 		Text:        form.Status, | ||||||
| 	} | 	} | ||||||
| 	c.JSON(http.StatusOK, mastoStatus) | 	c.JSON(http.StatusOK, mastoStatus) | ||||||
|  | @ -260,8 +268,8 @@ func parseVisibility(form *advancedStatusCreateForm, accountDefaultVis model.Vis | ||||||
| 	// Advanced takes priority if it's set. | 	// Advanced takes priority if it's set. | ||||||
| 	// If it's not set, take whatever masto visibility is 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 *that's* not set either, then just take the account default. | ||||||
| 	if form.AdvancedVisibility != nil && form.AdvancedVisibility.Visibility != nil { | 	if form.VisibilityAdvanced != nil { | ||||||
| 		gtsBasicVis = *form.AdvancedVisibility.Visibility | 		gtsBasicVis = *form.VisibilityAdvanced | ||||||
| 	} else if form.Visibility != "" { | 	} else if form.Visibility != "" { | ||||||
| 		gtsBasicVis = util.ParseGTSVisFromMastoVis(form.Visibility) | 		gtsBasicVis = util.ParseGTSVisFromMastoVis(form.Visibility) | ||||||
| 	} else { | 	} else { | ||||||
|  | @ -274,40 +282,38 @@ func parseVisibility(form *advancedStatusCreateForm, accountDefaultVis model.Vis | ||||||
| 		break | 		break | ||||||
| 	case model.VisibilityUnlocked: | 	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 | 		// 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.Federated != nil { | ||||||
| 			if form.AdvancedVisibility.Federated != nil { | 			gtsAdvancedVis.Federated = *form.Federated | ||||||
| 				gtsAdvancedVis.Federated = *form.AdvancedVisibility.Federated |  | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 			if form.AdvancedVisibility.Boostable != nil { | 		if form.Boostable != nil { | ||||||
| 				gtsAdvancedVis.Boostable = *form.AdvancedVisibility.Boostable | 			gtsAdvancedVis.Boostable = *form.Boostable | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 			if form.AdvancedVisibility.Replyable != nil { | 		if form.Replyable != nil { | ||||||
| 				gtsAdvancedVis.Replyable = *form.AdvancedVisibility.Replyable | 			gtsAdvancedVis.Replyable = *form.Replyable | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 			if form.AdvancedVisibility.Likeable != nil { | 		if form.Likeable != nil { | ||||||
| 				gtsAdvancedVis.Likeable = *form.AdvancedVisibility.Likeable | 			gtsAdvancedVis.Likeable = *form.Likeable | ||||||
| 			} |  | ||||||
| 		} | 		} | ||||||
|  | 
 | ||||||
| 	case model.VisibilityFollowersOnly, model.VisibilityMutualsOnly: | 	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 | 		// 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 | 		gtsAdvancedVis.Boostable = false | ||||||
| 
 | 
 | ||||||
| 		if form.AdvancedVisibility != nil { | 		if form.Federated != nil { | ||||||
| 			if form.AdvancedVisibility.Federated != nil { | 			gtsAdvancedVis.Federated = *form.Federated | ||||||
| 				gtsAdvancedVis.Federated = *form.AdvancedVisibility.Federated |  | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 			if form.AdvancedVisibility.Replyable != nil { | 		if form.Replyable != nil { | ||||||
| 				gtsAdvancedVis.Replyable = *form.AdvancedVisibility.Replyable | 			gtsAdvancedVis.Replyable = *form.Replyable | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 			if form.AdvancedVisibility.Likeable != nil { | 		if form.Likeable != nil { | ||||||
| 				gtsAdvancedVis.Likeable = *form.AdvancedVisibility.Likeable | 			gtsAdvancedVis.Likeable = *form.Likeable | ||||||
| 			} |  | ||||||
| 		} | 		} | ||||||
|  | 
 | ||||||
| 	case model.VisibilityDirect: | 	case model.VisibilityDirect: | ||||||
| 		// direct is pretty easy: there's only one possible setting so return it | 		// direct is pretty easy: there's only one possible setting so return it | ||||||
| 		gtsAdvancedVis.Federated = true | 		gtsAdvancedVis.Federated = true | ||||||
|  | @ -336,9 +342,18 @@ func (m *statusModule) parseReplyToID(form *advancedStatusCreateForm, thisAccoun | ||||||
| 	repliedStatus := &model.Status{} | 	repliedStatus := &model.Status{} | ||||||
| 	repliedAccount := &model.Account{} | 	repliedAccount := &model.Account{} | ||||||
| 	// check replied status exists + is replyable | 	// check replied status exists + is replyable | ||||||
| 	if err := m.db.GetByID(form.InReplyToID, repliedStatus); err != nil || !repliedStatus.VisibilityAdvanced.Replyable { | 	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) | 			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 | 	// check replied account is known to us | ||||||
| 	if err := m.db.GetByID(repliedStatus.AccountID, repliedAccount); err != nil { | 	if err := m.db.GetByID(repliedStatus.AccountID, repliedAccount); err != nil { | ||||||
| 		return fmt.Errorf("status with id %s not replyable: %s", form.InReplyToID, err) | 		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 | 	status.Attachments = attachments | ||||||
| 	return nil | 	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 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -168,24 +168,10 @@ func (suite *StatusCreateTestSuite) TestStatusCreatePOSTHandlerSuccessful() { | ||||||
| 		"status":              {"this is a brand new status!"}, | 		"status":              {"this is a brand new status!"}, | ||||||
| 		"spoiler_text":        {"hello hello"}, | 		"spoiler_text":        {"hello hello"}, | ||||||
| 		"sensitive":           {"true"}, | 		"sensitive":           {"true"}, | ||||||
| 		"visibility":   {"public"}, | 		"visibility_advanced": {"mutuals_only"}, | ||||||
| 		// 	Status string `form:"status"` | 		"likeable":            {"false"}, | ||||||
| 		// // Array of Attachment ids to be attached as media. If provided, status becomes optional, and poll cannot be used. | 		"replyable":           {"false"}, | ||||||
| 		// MediaIDs []string `form:"media_ids"` | 		"federated":           {"false"}, | ||||||
| 		// // 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"` |  | ||||||
| 	} | 	} | ||||||
| 	suite.statusModule.statusCreatePOSTHandler(ctx) | 	suite.statusModule.statusCreatePOSTHandler(ctx) | ||||||
| 
 | 
 | ||||||
|  | @ -198,6 +184,7 @@ func (suite *StatusCreateTestSuite) TestStatusCreatePOSTHandlerSuccessful() { | ||||||
| 	defer result.Body.Close() | 	defer result.Body.Close() | ||||||
| 	b, err := ioutil.ReadAll(result.Body) | 	b, err := ioutil.ReadAll(result.Body) | ||||||
| 	assert.NoError(suite.T(), err) | 	assert.NoError(suite.T(), err) | ||||||
|  | 	fmt.Println(string(b)) | ||||||
| 
 | 
 | ||||||
| 	statusReply := &mastotypes.Status{} | 	statusReply := &mastotypes.Status{} | ||||||
| 	err = json.Unmarshal(b, statusReply) | 	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(), "hello hello", statusReply.SpoilerText) | ||||||
| 	assert.Equal(suite.T(), "this is a brand new status!", statusReply.Content) | 	assert.Equal(suite.T(), "this is a brand new status!", statusReply.Content) | ||||||
| 	assert.True(suite.T(), statusReply.Sensitive) | 	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) { | func TestStatusCreateTestSuite(t *testing.T) { | ||||||
|  |  | ||||||
|  | @ -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 { | 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 "" | 	return "" | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // ParseMastoVisFromGTSVis converts a gts visibility into its mastodon equivalent | ||||||
| func ParseMastoVisFromGTSVis(m model.Visibility) mastotypes.Visibility { | 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 "" | 	return "" | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue