diff options
| -rw-r--r-- | docs/api/swagger.yaml | 41 | ||||
| -rw-r--r-- | internal/api/client.go | 4 | ||||
| -rw-r--r-- | internal/api/client/preferences/preferences.go | 44 | ||||
| -rw-r--r-- | internal/api/client/preferences/preferencesget.go | 91 | ||||
| -rw-r--r-- | internal/api/model/preferences.go | 2 | ||||
| -rw-r--r-- | internal/processing/preferences.go | 57 | ||||
| -rw-r--r-- | internal/processing/preferences_test.go | 74 | ||||
| -rw-r--r-- | testrig/testmodels.go | 4 | 
8 files changed, 315 insertions, 2 deletions
diff --git a/docs/api/swagger.yaml b/docs/api/swagger.yaml index 25527439d..ddb31bc68 100644 --- a/docs/api/swagger.yaml +++ b/docs/api/swagger.yaml @@ -4651,6 +4651,47 @@ paths:              summary: Clear/delete all notifications for currently authorized user.              tags:                  - notifications +    /api/v1/preferences: +        get: +            description: |- +                Example: + +                ``` + +                { +                "posting:default:visibility": "public", +                "posting:default:sensitive": false, +                "posting:default:language": "en", +                "reading:expand:media": "default", +                "reading:expand:spoilers": false, +                "reading:autoplay:gifs": false +                } + +                ```` +            operationId: preferencesGet +            produces: +                - application/json +            responses: +                "200": +                    description: "" +                    schema: +                        type: object +                "400": +                    description: bad request +                "401": +                    description: unauthorized +                "404": +                    description: not found +                "406": +                    description: not acceptable +                "500": +                    description: internal server error +            security: +                - OAuth2 Bearer: +                    - read:accounts +            summary: Return an object of user preferences. +            tags: +                - preferences      /api/v1/reports:          get:              description: |- diff --git a/internal/api/client.go b/internal/api/client.go index 3750b41d4..59fa0cf96 100644 --- a/internal/api/client.go +++ b/internal/api/client.go @@ -35,6 +35,7 @@ import (  	"github.com/superseriousbusiness/gotosocial/internal/api/client/lists"  	"github.com/superseriousbusiness/gotosocial/internal/api/client/media"  	"github.com/superseriousbusiness/gotosocial/internal/api/client/notifications" +	"github.com/superseriousbusiness/gotosocial/internal/api/client/preferences"  	"github.com/superseriousbusiness/gotosocial/internal/api/client/reports"  	"github.com/superseriousbusiness/gotosocial/internal/api/client/search"  	"github.com/superseriousbusiness/gotosocial/internal/api/client/statuses" @@ -65,6 +66,7 @@ type Client struct {  	lists          *lists.Module          // api/v1/lists  	media          *media.Module          // api/v1/media, api/v2/media  	notifications  *notifications.Module  // api/v1/notifications +	preferences    *preferences.Module    // api/v1/preferences  	reports        *reports.Module        // api/v1/reports  	search         *search.Module         // api/v1/search, api/v2/search  	statuses       *statuses.Module       // api/v1/statuses @@ -101,6 +103,7 @@ func (c *Client) Route(r router.Router, m ...gin.HandlerFunc) {  	c.lists.Route(h)  	c.media.Route(h)  	c.notifications.Route(h) +	c.preferences.Route(h)  	c.reports.Route(h)  	c.search.Route(h)  	c.statuses.Route(h) @@ -128,6 +131,7 @@ func NewClient(db db.DB, p *processing.Processor) *Client {  		lists:          lists.New(p),  		media:          media.New(p),  		notifications:  notifications.New(p), +		preferences:    preferences.New(p),  		reports:        reports.New(p),  		search:         search.New(p),  		statuses:       statuses.New(p), diff --git a/internal/api/client/preferences/preferences.go b/internal/api/client/preferences/preferences.go new file mode 100644 index 000000000..4a1ba8fe6 --- /dev/null +++ b/internal/api/client/preferences/preferences.go @@ -0,0 +1,44 @@ +// GoToSocial +// Copyright (C) GoToSocial Authors admin@gotosocial.org +// SPDX-License-Identifier: AGPL-3.0-or-later +// +// 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 preferences + +import ( +	"net/http" + +	"github.com/gin-gonic/gin" +	"github.com/superseriousbusiness/gotosocial/internal/processing" +) + +const ( +	// BasePath is the base URI path for serving preferences, minus the api prefix. +	BasePath = "/v1/preferences" +) + +type Module struct { +	processor *processing.Processor +} + +func New(processor *processing.Processor) *Module { +	return &Module{ +		processor: processor, +	} +} + +func (m *Module) Route(attachHandler func(method string, path string, f ...gin.HandlerFunc) gin.IRoutes) { +	attachHandler(http.MethodGet, BasePath, m.PreferencesGETHandler) +} diff --git a/internal/api/client/preferences/preferencesget.go b/internal/api/client/preferences/preferencesget.go new file mode 100644 index 000000000..2834134de --- /dev/null +++ b/internal/api/client/preferences/preferencesget.go @@ -0,0 +1,91 @@ +// GoToSocial +// Copyright (C) GoToSocial Authors admin@gotosocial.org +// SPDX-License-Identifier: AGPL-3.0-or-later +// +// 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 preferences + +import ( +	"net/http" + +	"github.com/gin-gonic/gin" +	apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util" +	"github.com/superseriousbusiness/gotosocial/internal/gtserror" +	"github.com/superseriousbusiness/gotosocial/internal/oauth" +) + +// PreferencesGETHandler swagger:operation GET /api/v1/preferences preferencesGet +// +// Return an object of user preferences. +// +// Example: +// +// ``` +// +//	{ +//		 "posting:default:visibility": "public", +//		 "posting:default:sensitive": false, +//		 "posting:default:language": "en", +//		 "reading:expand:media": "default", +//		 "reading:expand:spoilers": false, +//		 "reading:autoplay:gifs": false +//	} +// +// ```` +// +//	--- +//	tags: +//	- preferences +// +//	produces: +//	- application/json +// +//	security: +//	- OAuth2 Bearer: +//		- read:accounts +// +//	responses: +//		'200': +//			schema: +//				type: object +//		'400': +//			description: bad request +//		'401': +//			description: unauthorized +//		'404': +//			description: not found +//		'406': +//			description: not acceptable +//		'500': +//			description: internal server error +func (m *Module) PreferencesGETHandler(c *gin.Context) { +	authed, err := oauth.Authed(c, false, false, false, true) +	if err != nil { +		apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGetV1) +		return +	} + +	if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil { +		apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGetV1) +		return +	} + +	resp, errWithCode := m.processor.PreferencesGet(c.Request.Context(), authed.Account.ID) +	if errWithCode != nil { +		apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1) +		return +	} +	c.JSON(http.StatusOK, resp) +} diff --git a/internal/api/model/preferences.go b/internal/api/model/preferences.go index 7a4c96b18..35ad040a2 100644 --- a/internal/api/model/preferences.go +++ b/internal/api/model/preferences.go @@ -36,4 +36,6 @@ type Preferences struct {  	ReadingExpandMedia string `json:"reading:expand:media"`  	// Whether CWs should be expanded by default.  	ReadingExpandSpoilers bool `json:"reading:expand:spoilers"` +	// Whether gifs should automatically play. +	ReadingAutoPlayGifs bool `json:"reading:autoplay:gifs"`  } diff --git a/internal/processing/preferences.go b/internal/processing/preferences.go new file mode 100644 index 000000000..90fc86430 --- /dev/null +++ b/internal/processing/preferences.go @@ -0,0 +1,57 @@ +// GoToSocial +// Copyright (C) GoToSocial Authors admin@gotosocial.org +// SPDX-License-Identifier: AGPL-3.0-or-later +// +// 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 processing + +import ( +	"context" + +	apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" +	"github.com/superseriousbusiness/gotosocial/internal/gtserror" +	"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" +) + +func (p *Processor) PreferencesGet(ctx context.Context, accountID string) (*apimodel.Preferences, gtserror.WithCode) { +	act, err := p.state.DB.GetAccountByID(ctx, accountID) +	if err != nil { +		return nil, gtserror.NewErrorInternalError(err) +	} + +	return &apimodel.Preferences{ +		PostingDefaultVisibility: mastoPrefVisibility(act.Privacy), +		PostingDefaultSensitive:  *act.Sensitive, +		PostingDefaultLanguage:   act.Language, +		// The Reading* preferences don't appear to actually be settable by the +		// client, so forcing some sensible defaults here +		ReadingExpandMedia:    "default", +		ReadingExpandSpoilers: false, +		ReadingAutoPlayGifs:   false, +	}, nil +} + +func mastoPrefVisibility(vis gtsmodel.Visibility) string { +	switch vis { +	case gtsmodel.VisibilityPublic, gtsmodel.VisibilityDirect: +		return string(vis) +	case gtsmodel.VisibilityUnlocked: +		return "unlisted" +	default: +		// this will catch gtsmodel.VisibilityMutualsOnly and other types Mastodon doesn't +		// have and map them to the most restrictive state +		return "private" +	} +} diff --git a/internal/processing/preferences_test.go b/internal/processing/preferences_test.go new file mode 100644 index 000000000..be88b5edf --- /dev/null +++ b/internal/processing/preferences_test.go @@ -0,0 +1,74 @@ +// GoToSocial +// Copyright (C) GoToSocial Authors admin@gotosocial.org +// SPDX-License-Identifier: AGPL-3.0-or-later +// +// 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 processing_test + +import ( +	"context" +	"testing" + +	"github.com/stretchr/testify/suite" +	"github.com/superseriousbusiness/gotosocial/internal/api/model" +	"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" +) + +type PreferencesTestSuite struct { +	ProcessingStandardTestSuite +} + +func (suite *PreferencesTestSuite) TestPreferencesGet() { +	ctx := context.Background() +	tests := []struct { +		act   *gtsmodel.Account +		prefs *model.Preferences +	}{ +		{ +			act: suite.testAccounts["local_account_1"], +			prefs: &model.Preferences{ +				PostingDefaultVisibility: "public", +				PostingDefaultSensitive:  false, +				PostingDefaultLanguage:   "en", +				ReadingExpandMedia:       "default", +				ReadingExpandSpoilers:    false, +				ReadingAutoPlayGifs:      false, +			}, +		}, +		{ +			act: suite.testAccounts["local_account_2"], +			prefs: &model.Preferences{ +				PostingDefaultVisibility: "private", +				PostingDefaultSensitive:  true, +				PostingDefaultLanguage:   "fr", +				ReadingExpandMedia:       "default", +				ReadingExpandSpoilers:    false, +				ReadingAutoPlayGifs:      false, +			}, +		}, +	} + +	for _, tt := range tests { +		suite.Run(tt.act.ID, func() { +			prefs, err := suite.processor.PreferencesGet(ctx, tt.act.ID) +			suite.NoError(err) +			suite.Equal(tt.prefs, prefs) +		}) +	} +} + +func TestPreferencesTestSuite(t *testing.T) { +	suite.Run(t, &PreferencesTestSuite{}) +} diff --git a/testrig/testmodels.go b/testrig/testmodels.go index 269bc9470..6638acbde 100644 --- a/testrig/testmodels.go +++ b/testrig/testmodels.go @@ -487,8 +487,8 @@ func NewTestAccounts() map[string]*gtsmodel.Account {  			Locked:                  TrueBool(),  			Discoverable:            FalseBool(),  			Privacy:                 gtsmodel.VisibilityFollowersOnly, -			Sensitive:               FalseBool(), -			Language:                "en", +			Sensitive:               TrueBool(), +			Language:                "fr",  			URI:                     "http://localhost:8080/users/1happyturtle",  			URL:                     "http://localhost:8080/@1happyturtle",  			FetchedAt:               time.Time{},  | 
