diff options
Diffstat (limited to 'internal/api')
25 files changed, 169 insertions, 200 deletions
diff --git a/internal/api/client/admin/accountaction.go b/internal/api/client/admin/accountaction.go index 7d74e8530..64e6c39ca 100644 --- a/internal/api/client/admin/accountaction.go +++ b/internal/api/client/admin/accountaction.go @@ -116,10 +116,9 @@ func (m *Module) AccountActionPOSTHandler(c *gin.Context) {  		return  	} -	targetAcctID := c.Param(IDKey) -	if targetAcctID == "" { -		err := errors.New("no account id specified") -		apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGetV1) +	targetAcctID, errWithCode := apiutil.ParseID(c.Param(apiutil.IDKey)) +	if errWithCode != nil { +		apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)  		return  	}  	form.TargetID = targetAcctID diff --git a/internal/api/client/admin/admin.go b/internal/api/client/admin/admin.go index ef54f0b50..2c55de2f0 100644 --- a/internal/api/client/admin/admin.go +++ b/internal/api/client/admin/admin.go @@ -22,6 +22,7 @@ import (  	"codeberg.org/gruf/go-debug"  	"github.com/gin-gonic/gin" +	apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"  	"github.com/superseriousbusiness/gotosocial/internal/processing"  	"github.com/superseriousbusiness/gotosocial/internal/state"  ) @@ -29,48 +30,40 @@ import (  const (  	BasePath                = "/v1/admin"  	EmojiPath               = BasePath + "/custom_emojis" -	EmojiPathWithID         = EmojiPath + "/:" + IDKey +	EmojiPathWithID         = EmojiPath + "/:" + apiutil.IDKey  	EmojiCategoriesPath     = EmojiPath + "/categories"  	DomainBlocksPath        = BasePath + "/domain_blocks" -	DomainBlocksPathWithID  = DomainBlocksPath + "/:" + IDKey +	DomainBlocksPathWithID  = DomainBlocksPath + "/:" + apiutil.IDKey  	DomainAllowsPath        = BasePath + "/domain_allows" -	DomainAllowsPathWithID  = DomainAllowsPath + "/:" + IDKey +	DomainAllowsPathWithID  = DomainAllowsPath + "/:" + apiutil.IDKey  	DomainKeysExpirePath    = BasePath + "/domain_keys_expire"  	HeaderAllowsPath        = BasePath + "/header_allows" -	HeaderAllowsPathWithID  = HeaderAllowsPath + "/:" + IDKey +	HeaderAllowsPathWithID  = HeaderAllowsPath + "/:" + apiutil.IDKey  	HeaderBlocksPath        = BasePath + "/header_blocks" -	HeaderBlocksPathWithID  = HeaderBlocksPath + "/:" + IDKey +	HeaderBlocksPathWithID  = HeaderBlocksPath + "/:" + apiutil.IDKey  	AccountsV1Path          = BasePath + "/accounts"  	AccountsV2Path          = "/v2/admin/accounts" -	AccountsPathWithID      = AccountsV1Path + "/:" + IDKey +	AccountsPathWithID      = AccountsV1Path + "/:" + apiutil.IDKey  	AccountsActionPath      = AccountsPathWithID + "/action"  	AccountsApprovePath     = AccountsPathWithID + "/approve"  	AccountsRejectPath      = AccountsPathWithID + "/reject"  	MediaCleanupPath        = BasePath + "/media_cleanup"  	MediaRefetchPath        = BasePath + "/media_refetch"  	ReportsPath             = BasePath + "/reports" -	ReportsPathWithID       = ReportsPath + "/:" + IDKey +	ReportsPathWithID       = ReportsPath + "/:" + apiutil.IDKey  	ReportsResolvePath      = ReportsPathWithID + "/resolve"  	EmailPath               = BasePath + "/email"  	EmailTestPath           = EmailPath + "/test"  	InstanceRulesPath       = BasePath + "/instance/rules" -	InstanceRulesPathWithID = InstanceRulesPath + "/:" + IDKey +	InstanceRulesPathWithID = InstanceRulesPath + "/:" + apiutil.IDKey  	DebugPath               = BasePath + "/debug"  	DebugAPUrlPath          = DebugPath + "/apurl"  	DebugClearCachesPath    = DebugPath + "/caches/clear" -	IDKey                 = "id"  	FilterQueryKey        = "filter"  	MaxShortcodeDomainKey = "max_shortcode_domain"  	MinShortcodeDomainKey = "min_shortcode_domain" -	LimitKey              = "limit"  	DomainQueryKey        = "domain" -	ResolvedKey           = "resolved" -	AccountIDKey          = "account_id" -	TargetAccountIDKey    = "target_account_id" -	MaxIDKey              = "max_id" -	SinceIDKey            = "since_id" -	MinIDKey              = "min_id"  )  type Module struct { diff --git a/internal/api/client/admin/emojidelete.go b/internal/api/client/admin/emojidelete.go index 47248a1b9..9f9f9d286 100644 --- a/internal/api/client/admin/emojidelete.go +++ b/internal/api/client/admin/emojidelete.go @@ -18,7 +18,6 @@  package admin  import ( -	"errors"  	"fmt"  	"net/http" @@ -97,10 +96,9 @@ func (m *Module) EmojiDELETEHandler(c *gin.Context) {  		return  	} -	emojiID := c.Param(IDKey) -	if emojiID == "" { -		err := errors.New("no emoji id specified") -		apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGetV1) +	emojiID, errWithCode := apiutil.ParseID(c.Param(apiutil.IDKey)) +	if errWithCode != nil { +		apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)  		return  	} diff --git a/internal/api/client/admin/emojidelete_test.go b/internal/api/client/admin/emojidelete_test.go index e27592baf..10cf3fe8d 100644 --- a/internal/api/client/admin/emojidelete_test.go +++ b/internal/api/client/admin/emojidelete_test.go @@ -28,6 +28,7 @@ import (  	"github.com/stretchr/testify/suite"  	"github.com/superseriousbusiness/gotosocial/internal/api/client/admin" +	apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"  	"github.com/superseriousbusiness/gotosocial/internal/db"  ) @@ -41,7 +42,7 @@ func (suite *EmojiDeleteTestSuite) TestEmojiDelete1() {  	path := admin.EmojiPathWithID  	ctx := suite.newContext(recorder, http.MethodDelete, nil, path, "application/json") -	ctx.AddParam(admin.IDKey, testEmoji.ID) +	ctx.AddParam(apiutil.IDKey, testEmoji.ID)  	suite.adminModule.EmojiDELETEHandler(ctx)  	suite.Equal(http.StatusOK, recorder.Code) @@ -78,7 +79,7 @@ func (suite *EmojiDeleteTestSuite) TestEmojiDelete2() {  	path := admin.EmojiPathWithID  	ctx := suite.newContext(recorder, http.MethodDelete, nil, path, "application/json") -	ctx.AddParam(admin.IDKey, testEmoji.ID) +	ctx.AddParam(apiutil.IDKey, testEmoji.ID)  	suite.adminModule.EmojiDELETEHandler(ctx)  	suite.Equal(http.StatusBadRequest, recorder.Code) @@ -100,7 +101,7 @@ func (suite *EmojiDeleteTestSuite) TestEmojiDeleteNotFound() {  	path := admin.EmojiPathWithID  	ctx := suite.newContext(recorder, http.MethodDelete, nil, path, "application/json") -	ctx.AddParam(admin.IDKey, "01GF8VRXX1R00X7XH8973Z29R1") +	ctx.AddParam(apiutil.IDKey, "01GF8VRXX1R00X7XH8973Z29R1")  	suite.adminModule.EmojiDELETEHandler(ctx)  	suite.Equal(http.StatusNotFound, recorder.Code) diff --git a/internal/api/client/admin/emojiget.go b/internal/api/client/admin/emojiget.go index 730d92db1..7ecbcfa19 100644 --- a/internal/api/client/admin/emojiget.go +++ b/internal/api/client/admin/emojiget.go @@ -18,7 +18,6 @@  package admin  import ( -	"errors"  	"fmt"  	"net/http" @@ -82,10 +81,9 @@ func (m *Module) EmojiGETHandler(c *gin.Context) {  		return  	} -	emojiID := c.Param(IDKey) -	if emojiID == "" { -		err := errors.New("no emoji id specified") -		apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGetV1) +	emojiID, errWithCode := apiutil.ParseID(c.Param(apiutil.IDKey)) +	if errWithCode != nil { +		apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)  		return  	} diff --git a/internal/api/client/admin/emojiget_test.go b/internal/api/client/admin/emojiget_test.go index 86c847b17..b8bad2536 100644 --- a/internal/api/client/admin/emojiget_test.go +++ b/internal/api/client/admin/emojiget_test.go @@ -27,6 +27,7 @@ import (  	"github.com/stretchr/testify/suite"  	"github.com/superseriousbusiness/gotosocial/internal/api/client/admin" +	apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"  )  type EmojiGetTestSuite struct { @@ -39,7 +40,7 @@ func (suite *EmojiGetTestSuite) TestEmojiGet1() {  	path := admin.EmojiPathWithID  	ctx := suite.newContext(recorder, http.MethodGet, nil, path, "application/json") -	ctx.AddParam(admin.IDKey, testEmoji.ID) +	ctx.AddParam(apiutil.IDKey, testEmoji.ID)  	suite.adminModule.EmojiGETHandler(ctx)  	suite.Equal(http.StatusOK, recorder.Code) @@ -71,7 +72,7 @@ func (suite *EmojiGetTestSuite) TestEmojiGet2() {  	path := admin.EmojiPathWithID  	ctx := suite.newContext(recorder, http.MethodGet, nil, path, "application/json") -	ctx.AddParam(admin.IDKey, testEmoji.ID) +	ctx.AddParam(apiutil.IDKey, testEmoji.ID)  	suite.adminModule.EmojiGETHandler(ctx)  	suite.Equal(http.StatusOK, recorder.Code) @@ -102,7 +103,7 @@ func (suite *EmojiGetTestSuite) TestEmojiGetNotFound() {  	path := admin.EmojiPathWithID  	ctx := suite.newContext(recorder, http.MethodGet, nil, path, "application/json") -	ctx.AddParam(admin.IDKey, "01GF8VRXX1R00X7XH8973Z29R1") +	ctx.AddParam(apiutil.IDKey, "01GF8VRXX1R00X7XH8973Z29R1")  	suite.adminModule.EmojiGETHandler(ctx)  	suite.Equal(http.StatusNotFound, recorder.Code) diff --git a/internal/api/client/admin/emojisget.go b/internal/api/client/admin/emojisget.go index 4013e1836..d50b553ac 100644 --- a/internal/api/client/admin/emojisget.go +++ b/internal/api/client/admin/emojisget.go @@ -20,7 +20,6 @@ package admin  import (  	"fmt"  	"net/http" -	"strconv"  	"strings"  	"github.com/gin-gonic/gin" @@ -76,6 +75,8 @@ import (  //		type: integer  //		description: Number of emojis to return. Less than 1, or not set, means unlimited (all emojis).  //		default: 50 +//		minimum: 0 +//		maximum: 200  //		in: query  //	-  //		name: max_shortcode_domain @@ -142,19 +143,10 @@ func (m *Module) EmojisGETHandler(c *gin.Context) {  	maxShortcodeDomain := c.Query(MaxShortcodeDomainKey)  	minShortcodeDomain := c.Query(MinShortcodeDomainKey) -	limit := 50 -	limitString := c.Query(LimitKey) -	if limitString != "" { -		i, err := strconv.ParseInt(limitString, 10, 32) -		if err != nil { -			err := fmt.Errorf("error parsing %s: %s", LimitKey, err) -			apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGetV1) -			return -		} -		limit = int(i) -	} -	if limit < 0 { -		limit = 0 +	limit, errWithCode := apiutil.ParseLimit(c.Query(apiutil.LimitKey), 50, 200, 0) +	if errWithCode != nil { +		apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1) +		return  	}  	var domain string diff --git a/internal/api/client/admin/emojiupdate.go b/internal/api/client/admin/emojiupdate.go index 1d41dd545..37f67cabd 100644 --- a/internal/api/client/admin/emojiupdate.go +++ b/internal/api/client/admin/emojiupdate.go @@ -147,10 +147,9 @@ func (m *Module) EmojiPATCHHandler(c *gin.Context) {  		return  	} -	emojiID := c.Param(IDKey) -	if emojiID == "" { -		err := errors.New("no emoji id specified") -		apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGetV1) +	emojiID, errWithCode := apiutil.ParseID(c.Param(apiutil.IDKey)) +	if errWithCode != nil { +		apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)  		return  	} diff --git a/internal/api/client/admin/emojiupdate_test.go b/internal/api/client/admin/emojiupdate_test.go index eb9d9d866..676363e39 100644 --- a/internal/api/client/admin/emojiupdate_test.go +++ b/internal/api/client/admin/emojiupdate_test.go @@ -28,6 +28,7 @@ import (  	"github.com/stretchr/testify/suite"  	"github.com/superseriousbusiness/gotosocial/internal/api/client/admin"  	apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" +	apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"  	"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"  	"github.com/superseriousbusiness/gotosocial/testrig"  ) @@ -53,7 +54,7 @@ func (suite *EmojiUpdateTestSuite) TestEmojiUpdateNewCategory() {  	bodyBytes := requestBody.Bytes()  	recorder := httptest.NewRecorder()  	ctx := suite.newContext(recorder, http.MethodPost, bodyBytes, admin.EmojiPathWithID, w.FormDataContentType()) -	ctx.AddParam(admin.IDKey, testEmoji.ID) +	ctx.AddParam(apiutil.IDKey, testEmoji.ID)  	// call the handler  	suite.adminModule.EmojiPATCHHandler(ctx) @@ -130,7 +131,7 @@ func (suite *EmojiUpdateTestSuite) TestEmojiUpdateSwitchCategory() {  	bodyBytes := requestBody.Bytes()  	recorder := httptest.NewRecorder()  	ctx := suite.newContext(recorder, http.MethodPost, bodyBytes, admin.EmojiPathWithID, w.FormDataContentType()) -	ctx.AddParam(admin.IDKey, testEmoji.ID) +	ctx.AddParam(apiutil.IDKey, testEmoji.ID)  	// call the handler  	suite.adminModule.EmojiPATCHHandler(ctx) @@ -208,7 +209,7 @@ func (suite *EmojiUpdateTestSuite) TestEmojiUpdateCopyRemoteToLocal() {  	bodyBytes := requestBody.Bytes()  	recorder := httptest.NewRecorder()  	ctx := suite.newContext(recorder, http.MethodPost, bodyBytes, admin.EmojiPathWithID, w.FormDataContentType()) -	ctx.AddParam(admin.IDKey, testEmoji.ID) +	ctx.AddParam(apiutil.IDKey, testEmoji.ID)  	// call the handler  	suite.adminModule.EmojiPATCHHandler(ctx) @@ -284,7 +285,7 @@ func (suite *EmojiUpdateTestSuite) TestEmojiUpdateDisableEmoji() {  	bodyBytes := requestBody.Bytes()  	recorder := httptest.NewRecorder()  	ctx := suite.newContext(recorder, http.MethodPost, bodyBytes, admin.EmojiPathWithID, w.FormDataContentType()) -	ctx.AddParam(admin.IDKey, testEmoji.ID) +	ctx.AddParam(apiutil.IDKey, testEmoji.ID)  	// call the handler  	suite.adminModule.EmojiPATCHHandler(ctx) @@ -325,7 +326,7 @@ func (suite *EmojiUpdateTestSuite) TestEmojiUpdateDisableLocalEmoji() {  	bodyBytes := requestBody.Bytes()  	recorder := httptest.NewRecorder()  	ctx := suite.newContext(recorder, http.MethodPost, bodyBytes, admin.EmojiPathWithID, w.FormDataContentType()) -	ctx.AddParam(admin.IDKey, testEmoji.ID) +	ctx.AddParam(apiutil.IDKey, testEmoji.ID)  	// call the handler  	suite.adminModule.EmojiPATCHHandler(ctx) @@ -358,7 +359,7 @@ func (suite *EmojiUpdateTestSuite) TestEmojiUpdateModifyRemoteEmoji() {  	bodyBytes := requestBody.Bytes()  	recorder := httptest.NewRecorder()  	ctx := suite.newContext(recorder, http.MethodPost, bodyBytes, admin.EmojiPathWithID, w.FormDataContentType()) -	ctx.AddParam(admin.IDKey, testEmoji.ID) +	ctx.AddParam(apiutil.IDKey, testEmoji.ID)  	// call the handler  	suite.adminModule.EmojiPATCHHandler(ctx) @@ -391,7 +392,7 @@ func (suite *EmojiUpdateTestSuite) TestEmojiUpdateModifyNoParams() {  	bodyBytes := requestBody.Bytes()  	recorder := httptest.NewRecorder()  	ctx := suite.newContext(recorder, http.MethodPost, bodyBytes, admin.EmojiPathWithID, w.FormDataContentType()) -	ctx.AddParam(admin.IDKey, testEmoji.ID) +	ctx.AddParam(apiutil.IDKey, testEmoji.ID)  	// call the handler  	suite.adminModule.EmojiPATCHHandler(ctx) @@ -425,7 +426,7 @@ func (suite *EmojiUpdateTestSuite) TestEmojiUpdateCopyLocalToLocal() {  	bodyBytes := requestBody.Bytes()  	recorder := httptest.NewRecorder()  	ctx := suite.newContext(recorder, http.MethodPost, bodyBytes, admin.EmojiPathWithID, w.FormDataContentType()) -	ctx.AddParam(admin.IDKey, testEmoji.ID) +	ctx.AddParam(apiutil.IDKey, testEmoji.ID)  	// call the handler  	suite.adminModule.EmojiPATCHHandler(ctx) @@ -459,7 +460,7 @@ func (suite *EmojiUpdateTestSuite) TestEmojiUpdateCopyEmptyShortcode() {  	bodyBytes := requestBody.Bytes()  	recorder := httptest.NewRecorder()  	ctx := suite.newContext(recorder, http.MethodPost, bodyBytes, admin.EmojiPathWithID, w.FormDataContentType()) -	ctx.AddParam(admin.IDKey, testEmoji.ID) +	ctx.AddParam(apiutil.IDKey, testEmoji.ID)  	// call the handler  	suite.adminModule.EmojiPATCHHandler(ctx) @@ -492,7 +493,7 @@ func (suite *EmojiUpdateTestSuite) TestEmojiUpdateCopyNoShortcode() {  	bodyBytes := requestBody.Bytes()  	recorder := httptest.NewRecorder()  	ctx := suite.newContext(recorder, http.MethodPost, bodyBytes, admin.EmojiPathWithID, w.FormDataContentType()) -	ctx.AddParam(admin.IDKey, testEmoji.ID) +	ctx.AddParam(apiutil.IDKey, testEmoji.ID)  	// call the handler  	suite.adminModule.EmojiPATCHHandler(ctx) @@ -526,7 +527,7 @@ func (suite *EmojiUpdateTestSuite) TestEmojiUpdateCopyShortcodeAlreadyInUse() {  	bodyBytes := requestBody.Bytes()  	recorder := httptest.NewRecorder()  	ctx := suite.newContext(recorder, http.MethodPost, bodyBytes, admin.EmojiPathWithID, w.FormDataContentType()) -	ctx.AddParam(admin.IDKey, testEmoji.ID) +	ctx.AddParam(apiutil.IDKey, testEmoji.ID)  	// call the handler  	suite.adminModule.EmojiPATCHHandler(ctx) diff --git a/internal/api/client/admin/reportget.go b/internal/api/client/admin/reportget.go index f70ae8b54..f2acd214c 100644 --- a/internal/api/client/admin/reportget.go +++ b/internal/api/client/admin/reportget.go @@ -18,7 +18,6 @@  package admin  import ( -	"errors"  	"fmt"  	"net/http" @@ -85,10 +84,9 @@ func (m *Module) ReportGETHandler(c *gin.Context) {  		return  	} -	reportID := c.Param(IDKey) -	if reportID == "" { -		err := errors.New("no report id specified") -		apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGetV1) +	reportID, errWithCode := apiutil.ParseID(c.Param(apiutil.IDKey)) +	if errWithCode != nil { +		apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)  		return  	} diff --git a/internal/api/client/admin/reportresolve.go b/internal/api/client/admin/reportresolve.go index 51c268a2d..f17ae24be 100644 --- a/internal/api/client/admin/reportresolve.go +++ b/internal/api/client/admin/reportresolve.go @@ -18,7 +18,6 @@  package admin  import ( -	"errors"  	"fmt"  	"net/http" @@ -107,10 +106,9 @@ func (m *Module) ReportResolvePOSTHandler(c *gin.Context) {  		return  	} -	reportID := c.Param(IDKey) -	if reportID == "" { -		err := errors.New("no report id specified") -		apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGetV1) +	reportID, errWithCode := apiutil.ParseID(c.Param(apiutil.IDKey)) +	if errWithCode != nil { +		apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)  		return  	} diff --git a/internal/api/client/admin/reportresolve_test.go b/internal/api/client/admin/reportresolve_test.go index 087080a70..561661fe0 100644 --- a/internal/api/client/admin/reportresolve_test.go +++ b/internal/api/client/admin/reportresolve_test.go @@ -29,6 +29,7 @@ import (  	"github.com/stretchr/testify/suite"  	"github.com/superseriousbusiness/gotosocial/internal/api/client/admin"  	apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" +	apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"  	"github.com/superseriousbusiness/gotosocial/internal/config"  	"github.com/superseriousbusiness/gotosocial/internal/gtserror"  	"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" @@ -65,7 +66,7 @@ func (suite *ReportResolveTestSuite) resolveReport(  	// create the request  	ctx.Request = httptest.NewRequest(http.MethodPost, requestURI, nil) -	ctx.AddParam(admin.IDKey, targetReportID) +	ctx.AddParam(apiutil.IDKey, targetReportID)  	ctx.Request.Header.Set("accept", "application/json")  	if actionTakenComment != nil {  		ctx.Request.Form = url.Values{"action_taken_comment": {*actionTakenComment}} diff --git a/internal/api/client/admin/reportsget.go b/internal/api/client/admin/reportsget.go index 58501a6d7..893960e2a 100644 --- a/internal/api/client/admin/reportsget.go +++ b/internal/api/client/admin/reportsget.go @@ -20,12 +20,12 @@ package admin  import (  	"fmt"  	"net/http" -	"strconv"  	"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" +	"github.com/superseriousbusiness/gotosocial/internal/paging"  )  // ReportsGETHandler swagger:operation GET /api/v1/admin/reports adminReports @@ -72,7 +72,7 @@ import (  //		name: max_id  //		type: string  //		description: >- -//			Return only reports *OLDER* than the given max ID. +//			Return only reports *OLDER* than the given max ID (for paging downwards).  //			The report with the specified ID will not be included in the response.  //		in: query  //	- @@ -81,23 +81,21 @@ import (  //		description: >-  //			Return only reports *NEWER* than the given since ID.  //			The report with the specified ID will not be included in the response. -//			This parameter is functionally equivalent to min_id.  //		in: query  //	-  //		name: min_id  //		type: string  //		description: >- -//			Return only reports *NEWER* than the given min ID. +//			Return only reports immediately *NEWER* than the given min ID (for paging upwards).  //			The report with the specified ID will not be included in the response. -//			This parameter is functionally equivalent to since_id.  //		in: query  //	-  //		name: limit  //		type: integer -//		description: >- -//			Number of reports to return. -//			If more than 100 or less than 1, will be clamped to 100. +//		description: Number of reports to return.  //		default: 20 +//		minimum: 1 +//		maximum: 100  //		in: query  //  //	security: @@ -144,34 +142,30 @@ func (m *Module) ReportsGETHandler(c *gin.Context) {  		return  	} -	var resolved *bool -	if resolvedString := c.Query(ResolvedKey); resolvedString != "" { -		i, err := strconv.ParseBool(resolvedString) -		if err != nil { -			err := fmt.Errorf("error parsing %s: %s", ResolvedKey, err) -			apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGetV1) -			return -		} -		resolved = &i +	resolved, errWithCode := apiutil.ParseResolved(c.Query(apiutil.ResolvedKey), nil) +	if errWithCode != nil { +		apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1) +		return  	} -	limit := 20 -	if limitString := c.Query(LimitKey); limitString != "" { -		i, err := strconv.Atoi(limitString) -		if err != nil { -			err := fmt.Errorf("error parsing %s: %s", LimitKey, err) -			apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGetV1) -			return -		} - -		// normalize -		if i < 1 || i > 100 { -			i = 100 -		} -		limit = i +	page, errWithCode := paging.ParseIDPage(c, +		1,   // min limit +		100, // max limit +		20,  // default limit +	) +	if errWithCode != nil { +		apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1) +		return  	} -	resp, errWithCode := m.processor.Admin().ReportsGet(c.Request.Context(), authed.Account, resolved, c.Query(AccountIDKey), c.Query(TargetAccountIDKey), c.Query(MaxIDKey), c.Query(SinceIDKey), c.Query(MinIDKey), limit) +	resp, errWithCode := m.processor.Admin().ReportsGet( +		c.Request.Context(), +		authed.Account, +		resolved, +		c.Query(apiutil.AccountIDKey), +		c.Query(apiutil.TargetAccountIDKey), +		page, +	)  	if errWithCode != nil {  		apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)  		return diff --git a/internal/api/client/admin/reportsget_test.go b/internal/api/client/admin/reportsget_test.go index b20921b36..28efabc00 100644 --- a/internal/api/client/admin/reportsget_test.go +++ b/internal/api/client/admin/reportsget_test.go @@ -28,6 +28,7 @@ import (  	"github.com/stretchr/testify/suite"  	"github.com/superseriousbusiness/gotosocial/internal/api/client/admin"  	apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" +	apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"  	"github.com/superseriousbusiness/gotosocial/internal/config"  	"github.com/superseriousbusiness/gotosocial/internal/gtserror"  	"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" @@ -63,24 +64,24 @@ func (suite *ReportsGetTestSuite) getReports(  	ctx.Set(oauth.SessionAuthorizedUser, user)  	// create the request URI -	requestPath := admin.ReportsPath + "?" + admin.LimitKey + "=" + strconv.Itoa(limit) +	requestPath := admin.ReportsPath + "?" + apiutil.LimitKey + "=" + strconv.Itoa(limit)  	if resolved != nil { -		requestPath = requestPath + "&" + admin.ResolvedKey + "=" + strconv.FormatBool(*resolved) +		requestPath = requestPath + "&" + apiutil.ResolvedKey + "=" + strconv.FormatBool(*resolved)  	}  	if accountID != "" { -		requestPath = requestPath + "&" + admin.AccountIDKey + "=" + accountID +		requestPath = requestPath + "&" + apiutil.AccountIDKey + "=" + accountID  	}  	if targetAccountID != "" { -		requestPath = requestPath + "&" + admin.TargetAccountIDKey + "=" + targetAccountID +		requestPath = requestPath + "&" + apiutil.TargetAccountIDKey + "=" + targetAccountID  	}  	if maxID != "" { -		requestPath = requestPath + "&" + admin.MaxIDKey + "=" + maxID +		requestPath = requestPath + "&" + apiutil.MaxIDKey + "=" + maxID  	}  	if sinceID != "" { -		requestPath = requestPath + "&" + admin.SinceIDKey + "=" + sinceID +		requestPath = requestPath + "&" + apiutil.SinceIDKey + "=" + sinceID  	}  	if minID != "" { -		requestPath = requestPath + "&" + admin.MinIDKey + "=" + minID +		requestPath = requestPath + "&" + apiutil.MinIDKey + "=" + minID  	}  	baseURI := config.GetProtocol() + "://" + config.GetHost()  	requestURI := baseURI + "/api/" + requestPath @@ -766,7 +767,7 @@ func (suite *ReportsGetTestSuite) TestReportsGetCreatedByAccount() {    }  ]`, string(b)) -	suite.Equal(`<http://localhost:8080/api/v1/admin/reports?limit=20&max_id=01GP3AWY4CRDVRNZKW0TEAMB5R&account_id=01F8MH5NBDF2MV7CTC4Q5128HF>; rel="next", <http://localhost:8080/api/v1/admin/reports?limit=20&min_id=01GP3AWY4CRDVRNZKW0TEAMB5R&account_id=01F8MH5NBDF2MV7CTC4Q5128HF>; rel="prev"`, link) +	suite.Equal(`<http://localhost:8080/api/v1/admin/reports?account_id=01F8MH5NBDF2MV7CTC4Q5128HF&limit=20&max_id=01GP3AWY4CRDVRNZKW0TEAMB5R>; rel="next", <http://localhost:8080/api/v1/admin/reports?account_id=01F8MH5NBDF2MV7CTC4Q5128HF&limit=20&min_id=01GP3AWY4CRDVRNZKW0TEAMB5R>; rel="prev"`, link)  }  func (suite *ReportsGetTestSuite) TestReportsGetTargetAccount() { @@ -1028,8 +1029,8 @@ func (suite *ReportsGetTestSuite) TestReportsGetZeroLimit() {  	suite.NoError(err)  	suite.Len(reports, 2) -	// Limit in Link header should be set to 100 -	suite.Equal(`<http://localhost:8080/api/v1/admin/reports?limit=100&max_id=01GP3AWY4CRDVRNZKW0TEAMB5R>; rel="next", <http://localhost:8080/api/v1/admin/reports?limit=100&min_id=01GP3DFY9XQ1TJMZT5BGAZPXX7>; rel="prev"`, link) +	// Limit in Link header should be set to default (20) +	suite.Equal(`<http://localhost:8080/api/v1/admin/reports?limit=20&max_id=01GP3AWY4CRDVRNZKW0TEAMB5R>; rel="next", <http://localhost:8080/api/v1/admin/reports?limit=20&min_id=01GP3DFY9XQ1TJMZT5BGAZPXX7>; rel="prev"`, link)  }  func (suite *ReportsGetTestSuite) TestReportsGetHighLimit() { diff --git a/internal/api/client/admin/ruledelete.go b/internal/api/client/admin/ruledelete.go index ead219e34..7e8fc0037 100644 --- a/internal/api/client/admin/ruledelete.go +++ b/internal/api/client/admin/ruledelete.go @@ -18,7 +18,6 @@  package admin  import ( -	"errors"  	"fmt"  	"net/http" @@ -95,10 +94,9 @@ func (m *Module) RuleDELETEHandler(c *gin.Context) {  		return  	} -	ruleID := c.Param(IDKey) -	if ruleID == "" { -		err := errors.New("no rule id specified") -		apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGetV1) +	ruleID, errWithCode := apiutil.ParseID(c.Param(apiutil.IDKey)) +	if errWithCode != nil { +		apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)  		return  	} diff --git a/internal/api/client/admin/ruleget.go b/internal/api/client/admin/ruleget.go index 8281092fb..28c0599f5 100644 --- a/internal/api/client/admin/ruleget.go +++ b/internal/api/client/admin/ruleget.go @@ -18,7 +18,6 @@  package admin  import ( -	"errors"  	"fmt"  	"net/http" @@ -85,10 +84,9 @@ func (m *Module) RuleGETHandler(c *gin.Context) {  		return  	} -	ruleID := c.Param(IDKey) -	if ruleID == "" { -		err := errors.New("no rule id specified") -		apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGetV1) +	ruleID, errWithCode := apiutil.ParseID(c.Param(apiutil.IDKey)) +	if errWithCode != nil { +		apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)  		return  	} diff --git a/internal/api/client/admin/ruleupdate.go b/internal/api/client/admin/ruleupdate.go index bf838f7ae..d58c30d94 100644 --- a/internal/api/client/admin/ruleupdate.go +++ b/internal/api/client/admin/ruleupdate.go @@ -18,7 +18,6 @@  package admin  import ( -	"errors"  	"fmt"  	"net/http" @@ -87,10 +86,9 @@ func (m *Module) RulePATCHHandler(c *gin.Context) {  		return  	} -	ruleID := c.Param(IDKey) -	if ruleID == "" { -		err := errors.New("no rule id specified") -		apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGetV1) +	ruleID, errWithCode := apiutil.ParseID(c.Param(apiutil.IDKey)) +	if errWithCode != nil { +		apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)  		return  	} diff --git a/internal/api/client/reports/reportget.go b/internal/api/client/reports/reportget.go index 4a9b06664..c9ca0054f 100644 --- a/internal/api/client/reports/reportget.go +++ b/internal/api/client/reports/reportget.go @@ -18,7 +18,6 @@  package reports  import ( -	"errors"  	"net/http"  	"github.com/gin-gonic/gin" @@ -77,10 +76,9 @@ func (m *Module) ReportGETHandler(c *gin.Context) {  		return  	} -	targetReportID := c.Param(IDKey) -	if targetReportID == "" { -		err := errors.New("no report id specified") -		apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGetV1) +	targetReportID, errWithCode := apiutil.ParseID(c.Param(apiutil.IDKey)) +	if errWithCode != nil { +		apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)  		return  	} diff --git a/internal/api/client/reports/reportget_test.go b/internal/api/client/reports/reportget_test.go index 5b6c406f7..4f01101b2 100644 --- a/internal/api/client/reports/reportget_test.go +++ b/internal/api/client/reports/reportget_test.go @@ -145,7 +145,7 @@ func (suite *ReportGetTestSuite) TestGetReport2() {  }  func (suite *ReportGetTestSuite) TestGetReport3() { -	report, err := suite.getReport(http.StatusBadRequest, `{"error":"Bad Request: no report id specified"}`, "") +	report, err := suite.getReport(http.StatusBadRequest, `{"error":"Bad Request: required key id was not set or had empty value"}`, "")  	suite.NoError(err)  	suite.Nil(report)  } diff --git a/internal/api/client/reports/reports.go b/internal/api/client/reports/reports.go index e881474fb..b10697c1f 100644 --- a/internal/api/client/reports/reports.go +++ b/internal/api/client/reports/reports.go @@ -21,19 +21,13 @@ import (  	"net/http"  	"github.com/gin-gonic/gin" +	apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"  	"github.com/superseriousbusiness/gotosocial/internal/processing"  )  const ( -	BasePath           = "/v1/reports" -	IDKey              = "id" -	ResolvedKey        = "resolved" -	TargetAccountIDKey = "target_account_id" -	MaxIDKey           = "max_id" -	SinceIDKey         = "since_id" -	MinIDKey           = "min_id" -	LimitKey           = "limit" -	BasePathWithID     = BasePath + "/:" + IDKey +	BasePath       = "/v1/reports" +	BasePathWithID = BasePath + "/:" + apiutil.IDKey  )  type Module struct { diff --git a/internal/api/client/reports/reportsget.go b/internal/api/client/reports/reportsget.go index 5f194a589..4c3d4e33a 100644 --- a/internal/api/client/reports/reportsget.go +++ b/internal/api/client/reports/reportsget.go @@ -18,14 +18,13 @@  package reports  import ( -	"fmt"  	"net/http" -	"strconv"  	"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" +	"github.com/superseriousbusiness/gotosocial/internal/paging"  )  // ReportsGETHandler swagger:operation GET /api/v1/reports reports @@ -67,7 +66,7 @@ import (  //		name: max_id  //		type: string  //		description: >- -//			Return only reports *OLDER* than the given max ID. +//			Return only reports *OLDER* than the given max ID (for paging downwards).  //			The report with the specified ID will not be included in the response.  //		in: query  //	- @@ -76,24 +75,21 @@ import (  //		description: >-  //			Return only reports *NEWER* than the given since ID.  //			The report with the specified ID will not be included in the response. -//			This parameter is functionally equivalent to min_id.  //		in: query  //	-  //		name: min_id  //		type: string  //		description: >- -//			Return only reports *NEWER* than the given min ID. +//			Return only reports immediately *NEWER* than the given min ID (for paging upwards).  //			The report with the specified ID will not be included in the response. -//			This parameter is functionally equivalent to since_id.  //		in: query  //	-  //		name: limit  //		type: integer -//		description: >- -//			Number of reports to return. -//			If less than 1, will be clamped to 1. -//			If more than 100, will be clamped to 100. +//		description: Number of reports to return.  //		default: 20 +//		minimum: 1 +//		maximum: 100  //		in: query  //  //	security: @@ -134,36 +130,29 @@ func (m *Module) ReportsGETHandler(c *gin.Context) {  		return  	} -	var resolved *bool -	if resolvedString := c.Query(ResolvedKey); resolvedString != "" { -		i, err := strconv.ParseBool(resolvedString) -		if err != nil { -			err := fmt.Errorf("error parsing %s: %s", ResolvedKey, err) -			apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGetV1) -			return -		} -		resolved = &i +	resolved, errWithCode := apiutil.ParseResolved(c.Query(apiutil.ResolvedKey), nil) +	if errWithCode != nil { +		apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1) +		return  	} -	limit := 20 -	if limitString := c.Query(LimitKey); limitString != "" { -		i, err := strconv.Atoi(limitString) -		if err != nil { -			err := fmt.Errorf("error parsing %s: %s", LimitKey, err) -			apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGetV1) -			return -		} - -		// normalize -		if i <= 0 { -			i = 1 -		} else if i >= 100 { -			i = 100 -		} -		limit = i +	page, errWithCode := paging.ParseIDPage(c, +		1,   // min limit +		100, // max limit +		20,  // default limit +	) +	if errWithCode != nil { +		apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1) +		return  	} -	resp, errWithCode := m.processor.Report().GetMultiple(c.Request.Context(), authed.Account, resolved, c.Query(TargetAccountIDKey), c.Query(MaxIDKey), c.Query(SinceIDKey), c.Query(MinIDKey), limit) +	resp, errWithCode := m.processor.Report().GetMultiple( +		c.Request.Context(), +		authed.Account, +		resolved, +		c.Query(apiutil.TargetAccountIDKey), +		page, +	)  	if errWithCode != nil {  		apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)  		return diff --git a/internal/api/client/reports/reportsget_test.go b/internal/api/client/reports/reportsget_test.go index c63d6c894..2413292a0 100644 --- a/internal/api/client/reports/reportsget_test.go +++ b/internal/api/client/reports/reportsget_test.go @@ -29,6 +29,7 @@ import (  	"github.com/stretchr/testify/suite"  	"github.com/superseriousbusiness/gotosocial/internal/api/client/reports"  	apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" +	apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"  	"github.com/superseriousbusiness/gotosocial/internal/config"  	"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"  	"github.com/superseriousbusiness/gotosocial/internal/oauth" @@ -61,21 +62,21 @@ func (suite *ReportsGetTestSuite) getReports(  	ctx.Set(oauth.SessionAuthorizedUser, user)  	// create the request URI -	requestPath := reports.BasePath + "?" + reports.LimitKey + "=" + strconv.Itoa(limit) +	requestPath := reports.BasePath + "?" + apiutil.LimitKey + "=" + strconv.Itoa(limit)  	if resolved != nil { -		requestPath = requestPath + "&" + reports.ResolvedKey + "=" + strconv.FormatBool(*resolved) +		requestPath = requestPath + "&" + apiutil.ResolvedKey + "=" + strconv.FormatBool(*resolved)  	}  	if targetAccountID != "" { -		requestPath = requestPath + "&" + reports.TargetAccountIDKey + "=" + targetAccountID +		requestPath = requestPath + "&" + apiutil.TargetAccountIDKey + "=" + targetAccountID  	}  	if maxID != "" { -		requestPath = requestPath + "&" + reports.MaxIDKey + "=" + maxID +		requestPath = requestPath + "&" + apiutil.MaxIDKey + "=" + maxID  	}  	if sinceID != "" { -		requestPath = requestPath + "&" + reports.SinceIDKey + "=" + sinceID +		requestPath = requestPath + "&" + apiutil.SinceIDKey + "=" + sinceID  	}  	if minID != "" { -		requestPath = requestPath + "&" + reports.MinIDKey + "=" + minID +		requestPath = requestPath + "&" + apiutil.MinIDKey + "=" + minID  	}  	baseURI := config.GetProtocol() + "://" + config.GetHost()  	requestURI := baseURI + "/api/" + requestPath diff --git a/internal/api/client/search/searchget.go b/internal/api/client/search/searchget.go index d7ab81388..0f9595efc 100644 --- a/internal/api/client/search/searchget.go +++ b/internal/api/client/search/searchget.go @@ -247,7 +247,7 @@ func (m *Module) SearchGETHandler(c *gin.Context) {  		Resolve:           resolve,  		Following:         following,  		ExcludeUnreviewed: excludeUnreviewed, -		AccountID:         c.Query(apiutil.SearchAccountIDKey), +		AccountID:         c.Query(apiutil.AccountIDKey),  		APIv1:             apiVersion == apiutil.APIv1,  	} diff --git a/internal/api/client/search/searchget_test.go b/internal/api/client/search/searchget_test.go index a0d0cad0e..27e5f782d 100644 --- a/internal/api/client/search/searchget_test.go +++ b/internal/api/client/search/searchget_test.go @@ -105,7 +105,7 @@ func (suite *SearchGetTestSuite) getSearch(  	}  	if fromAccountID != nil { -		queryParts = append(queryParts, apiutil.SearchAccountIDKey+"="+url.QueryEscape(*fromAccountID)) +		queryParts = append(queryParts, apiutil.AccountIDKey+"="+url.QueryEscape(*fromAccountID))  	}  	requestURL.RawQuery = strings.Join(queryParts, "&") diff --git a/internal/api/util/parsequery.go b/internal/api/util/parsequery.go index 5210735a1..90cc30e6f 100644 --- a/internal/api/util/parsequery.go +++ b/internal/api/util/parsequery.go @@ -34,13 +34,16 @@ const (  	/* Common keys */ -	IDKey       = "id" -	LimitKey    = "limit" -	LocalKey    = "local" -	MaxIDKey    = "max_id" -	SinceIDKey  = "since_id" -	MinIDKey    = "min_id" -	UsernameKey = "username" +	IDKey              = "id" +	LimitKey           = "limit" +	LocalKey           = "local" +	MaxIDKey           = "max_id" +	SinceIDKey         = "since_id" +	MinIDKey           = "min_id" +	UsernameKey        = "username" +	AccountIDKey       = "account_id" +	TargetAccountIDKey = "target_account_id" +	ResolvedKey        = "resolved"  	/* AP endpoint keys */ @@ -55,7 +58,6 @@ const (  	SearchQueryKey             = "q"  	SearchResolveKey           = "resolve"  	SearchTypeKey              = "type" -	SearchAccountIDKey         = "account_id"  	/* Tag keys */ @@ -132,6 +134,10 @@ func ParseLocal(value string, defaultValue bool) (bool, gtserror.WithCode) {  	return parseBool(value, defaultValue, LocalKey)  } +func ParseResolved(value string, defaultValue *bool) (*bool, gtserror.WithCode) { +	return parseBoolPtr(value, defaultValue, ResolvedKey) +} +  func ParseSearchExcludeUnreviewed(value string, defaultValue bool) (bool, gtserror.WithCode) {  	return parseBool(value, defaultValue, SearchExcludeUnreviewedKey)  } @@ -289,6 +295,19 @@ func parseBool(value string, defaultValue bool, key string) (bool, gtserror.With  	return i, nil  } +func parseBoolPtr(value string, defaultValue *bool, key string) (*bool, gtserror.WithCode) { +	if value == "" { +		return defaultValue, nil +	} + +	i, err := strconv.ParseBool(value) +	if err != nil { +		return defaultValue, parseError(key, value, defaultValue, err) +	} + +	return &i, nil +} +  func parseInt(value string, defaultValue int, max int, min int, key string) (int, gtserror.WithCode) {  	if value == "" {  		return defaultValue, nil  | 
