diff options
Diffstat (limited to 'internal/api')
| -rw-r--r-- | internal/api/client/instance/instance.go | 23 | ||||
| -rw-r--r-- | internal/api/client/instance/instance_test.go | 3 | ||||
| -rw-r--r-- | internal/api/client/instance/instanceget.go | 18 | ||||
| -rw-r--r-- | internal/api/client/instance/instancepatch.go | 18 | ||||
| -rw-r--r-- | internal/api/client/instance/instancepatch_test.go | 6 | ||||
| -rw-r--r-- | internal/api/client/instance/instancepeersget.go | 136 | ||||
| -rw-r--r-- | internal/api/client/instance/instancepeersget_test.go | 247 | ||||
| -rw-r--r-- | internal/api/model/domain.go (renamed from internal/api/model/domainblock.go) | 25 | 
8 files changed, 465 insertions, 11 deletions
diff --git a/internal/api/client/instance/instance.go b/internal/api/client/instance/instance.go index 758cce376..16ff7c9f9 100644 --- a/internal/api/client/instance/instance.go +++ b/internal/api/client/instance/instance.go @@ -1,3 +1,21 @@ +/* +   GoToSocial +   Copyright (C) 2021-2022 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 instance  import ( @@ -11,6 +29,10 @@ import (  const (  	// InstanceInformationPath is for serving instance info requests  	InstanceInformationPath = "api/v1/instance" +	// InstancePeersPath is for serving instance peers requests. +	InstancePeersPath = InstanceInformationPath + "/peers" +	// PeersFilterKey is used to provide filters to /api/v1/instance/peers +	PeersFilterKey = "filter"  )  // Module implements the ClientModule interface @@ -29,5 +51,6 @@ func New(processor processing.Processor) api.ClientModule {  func (m *Module) Route(s router.Router) error {  	s.AttachHandler(http.MethodGet, InstanceInformationPath, m.InstanceInformationGETHandler)  	s.AttachHandler(http.MethodPatch, InstanceInformationPath, m.InstanceUpdatePATCHHandler) +	s.AttachHandler(http.MethodGet, InstancePeersPath, m.InstancePeersGETHandler)  	return nil  } diff --git a/internal/api/client/instance/instance_test.go b/internal/api/client/instance/instance_test.go index 248b87761..1e5c59c97 100644 --- a/internal/api/client/instance/instance_test.go +++ b/internal/api/client/instance/instance_test.go @@ -21,7 +21,6 @@ package instance_test  import (  	"bytes"  	"fmt" -	"net/http"  	"net/http/httptest"  	"codeberg.org/gruf/go-store/kv" @@ -113,7 +112,7 @@ func (suite *InstanceStandardTestSuite) newContext(recorder *httptest.ResponseRe  	baseURI := fmt.Sprintf("%s://%s", protocol, host)  	requestURI := fmt.Sprintf("%s/%s", baseURI, requestPath) -	ctx.Request = httptest.NewRequest(http.MethodPatch, requestURI, bytes.NewReader(requestBody)) // the endpoint we're hitting +	ctx.Request = httptest.NewRequest(requestMethod, requestURI, bytes.NewReader(requestBody)) // the endpoint we're hitting  	if bodyContentType != "" {  		ctx.Request.Header.Set("Content-Type", bodyContentType) diff --git a/internal/api/client/instance/instanceget.go b/internal/api/client/instance/instanceget.go index 35e842102..5250b3b46 100644 --- a/internal/api/client/instance/instanceget.go +++ b/internal/api/client/instance/instanceget.go @@ -1,3 +1,21 @@ +/* +   GoToSocial +   Copyright (C) 2021-2022 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 instance  import ( diff --git a/internal/api/client/instance/instancepatch.go b/internal/api/client/instance/instancepatch.go index 4e3f1e454..6f1b3586b 100644 --- a/internal/api/client/instance/instancepatch.go +++ b/internal/api/client/instance/instancepatch.go @@ -1,3 +1,21 @@ +/* +   GoToSocial +   Copyright (C) 2021-2022 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 instance  import ( diff --git a/internal/api/client/instance/instancepatch_test.go b/internal/api/client/instance/instancepatch_test.go index 5ca4f2b7a..683fec5a6 100644 --- a/internal/api/client/instance/instancepatch_test.go +++ b/internal/api/client/instance/instancepatch_test.go @@ -63,7 +63,7 @@ func (suite *InstancePatchTestSuite) TestInstancePatch1() {  	b, err := io.ReadAll(result.Body)  	suite.NoError(err) -	suite.Equal(`{"uri":"http://localhost:8080","title":"Example Instance","description":"","short_description":"","email":"someone@example.org","version":"","registrations":true,"approval_required":true,"invites_enabled":false,"urls":{"streaming_api":"wss://localhost:8080"},"stats":{"domain_count":0,"status_count":16,"user_count":4},"thumbnail":"","contact_account":{"id":"01F8MH17FWEB39HZJ76B6VXSKF","username":"admin","acct":"admin","display_name":"","locked":false,"bot":false,"created_at":"2022-05-17T13:10:59.000Z","note":"","url":"http://localhost:8080/@admin","avatar":"","avatar_static":"","header":"","header_static":"","followers_count":1,"following_count":1,"statuses_count":4,"last_status_at":"2021-10-20T10:41:37.000Z","emojis":[],"fields":[]},"max_toot_chars":5000}`, string(b)) +	suite.Equal(`{"uri":"http://localhost:8080","title":"Example Instance","description":"","short_description":"","email":"someone@example.org","version":"","registrations":true,"approval_required":true,"invites_enabled":false,"urls":{"streaming_api":"wss://localhost:8080"},"stats":{"domain_count":2,"status_count":16,"user_count":4},"thumbnail":"","contact_account":{"id":"01F8MH17FWEB39HZJ76B6VXSKF","username":"admin","acct":"admin","display_name":"","locked":false,"bot":false,"created_at":"2022-05-17T13:10:59.000Z","note":"","url":"http://localhost:8080/@admin","avatar":"","avatar_static":"","header":"","header_static":"","followers_count":1,"following_count":1,"statuses_count":4,"last_status_at":"2021-10-20T10:41:37.000Z","emojis":[],"fields":[]},"max_toot_chars":5000}`, string(b))  }  func (suite *InstancePatchTestSuite) TestInstancePatch2() { @@ -93,7 +93,7 @@ func (suite *InstancePatchTestSuite) TestInstancePatch2() {  	b, err := io.ReadAll(result.Body)  	suite.NoError(err) -	suite.Equal(`{"uri":"http://localhost:8080","title":"Geoff's Instance","description":"","short_description":"","email":"","version":"","registrations":true,"approval_required":true,"invites_enabled":false,"urls":{"streaming_api":"wss://localhost:8080"},"stats":{"domain_count":0,"status_count":16,"user_count":4},"thumbnail":"","max_toot_chars":5000}`, string(b)) +	suite.Equal(`{"uri":"http://localhost:8080","title":"Geoff's Instance","description":"","short_description":"","email":"","version":"","registrations":true,"approval_required":true,"invites_enabled":false,"urls":{"streaming_api":"wss://localhost:8080"},"stats":{"domain_count":2,"status_count":16,"user_count":4},"thumbnail":"","max_toot_chars":5000}`, string(b))  }  func (suite *InstancePatchTestSuite) TestInstancePatch3() { @@ -123,7 +123,7 @@ func (suite *InstancePatchTestSuite) TestInstancePatch3() {  	b, err := io.ReadAll(result.Body)  	suite.NoError(err) -	suite.Equal(`{"uri":"http://localhost:8080","title":"localhost:8080","description":"","short_description":"\u003cp\u003eThis is some html, which is \u003cem\u003eallowed\u003c/em\u003e in short descriptions.\u003c/p\u003e","email":"","version":"","registrations":true,"approval_required":true,"invites_enabled":false,"urls":{"streaming_api":"wss://localhost:8080"},"stats":{"domain_count":0,"status_count":16,"user_count":4},"thumbnail":"","max_toot_chars":5000}`, string(b)) +	suite.Equal(`{"uri":"http://localhost:8080","title":"localhost:8080","description":"","short_description":"\u003cp\u003eThis is some html, which is \u003cem\u003eallowed\u003c/em\u003e in short descriptions.\u003c/p\u003e","email":"","version":"","registrations":true,"approval_required":true,"invites_enabled":false,"urls":{"streaming_api":"wss://localhost:8080"},"stats":{"domain_count":2,"status_count":16,"user_count":4},"thumbnail":"","max_toot_chars":5000}`, string(b))  }  func (suite *InstancePatchTestSuite) TestInstancePatch4() { diff --git a/internal/api/client/instance/instancepeersget.go b/internal/api/client/instance/instancepeersget.go new file mode 100644 index 000000000..d4d33d5bf --- /dev/null +++ b/internal/api/client/instance/instancepeersget.go @@ -0,0 +1,136 @@ +/* +   GoToSocial +   Copyright (C) 2021-2022 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 instance + +import ( +	"fmt" +	"net/http" +	"strings" + +	"github.com/superseriousbusiness/gotosocial/internal/api" +	"github.com/superseriousbusiness/gotosocial/internal/gtserror" +	"github.com/superseriousbusiness/gotosocial/internal/oauth" + +	"github.com/gin-gonic/gin" +) + +// InstancePeersGETHandler swagger:operation GET /api/v1/instance/peers instancePeersGet +// +// --- +// tags: +// - instance +// +// produces: +// - application/json +// +// parameters: +// - name: filter +//   type: string +//   description: |- +//     Comma-separated list of filters to apply to results. Recognized values are: +//     'open' -- include peers that are not suspended or silenced +//     'suspended' -- include peers that have been suspended. +//     If filter is 'open', only instances that haven't been suspended or silenced will be returned. +//     If filter is 'suspended', only suspended instances will be shown. +//     If filter is 'open,suspended', then all known instances will be returned. +//     If filter is an empty string or not set, then 'open' will be assumed as the default. +//   in: query +//   required: false +// +// responses: +//   '200': +//     description: |- +//       If no filter parameter is provided, or filter is empty, then a legacy, +//       Mastodon-API compatible response will be returned. This will consist of +//       just a 'flat' array of strings like `["example.com", "example.org"]`. +// +//       If a filter parameter is provided, then an array of objects with at least +//       a `domain` key set on each object will be returned. +// +//       Domains that are silenced or suspended will also have a key +//       'suspended_at' or 'silenced_at' that contains an iso8601 date string. +//       If one of these keys is not present on the domain object, it is open. +//       Suspended instances may in some cases be obfuscated, which means they +//       will have some letters replaced by '*' to make it more difficult for +//       bad actors to target instances with harassment. +// +//       Whether a flat response or a more detailed response is returned, domains +//       will be sorted alphabetically by hostname. +//     schema: +//       type: array +//       items: +//         "$ref": "#/definitions/domain" +//   '400': +//      description: bad request +//   '401': +//      description: unauthorized +//   '403': +//      description: forbidden +//   '404': +//      description: not found +//   '406': +//      description: not acceptable +//   '500': +//      description: internal server error +func (m *Module) InstancePeersGETHandler(c *gin.Context) { +	authed, err := oauth.Authed(c, false, false, false, false) +	if err != nil { +		api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet) +		return +	} + +	if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil { +		api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet) +		return +	} + +	var includeSuspended bool +	var includeOpen bool +	var flat bool +	if filterParam := c.Query(PeersFilterKey); filterParam != "" { +		filters := strings.Split(filterParam, ",") +		for _, f := range filters { +			trimmed := strings.TrimSpace(f) +			switch { +			case strings.EqualFold(trimmed, "suspended"): +				includeSuspended = true +			case strings.EqualFold(trimmed, "open"): +				includeOpen = true +			default: +				err := fmt.Errorf("filter %s not recognized; accepted values are 'open', 'suspended'", trimmed) +				api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet) +				return +			} +		} +	} else { +		// default is to only include open domains, and present +		// them in a 'flat' manner (just an array of strings), +		// to maintain compatibility with mastodon API +		includeOpen = true +		flat = true +	} + +	data, errWithCode := m.processor.InstancePeersGet(c.Request.Context(), authed, includeSuspended, includeOpen, flat) +	if errWithCode != nil { +		api.ErrorHandler(c, errWithCode, m.processor.InstanceGet) +		return +	} + +	c.JSON(http.StatusOK, data) +} diff --git a/internal/api/client/instance/instancepeersget_test.go b/internal/api/client/instance/instancepeersget_test.go new file mode 100644 index 000000000..cb35a9e50 --- /dev/null +++ b/internal/api/client/instance/instancepeersget_test.go @@ -0,0 +1,247 @@ +/* +   GoToSocial +   Copyright (C) 2021-2022 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 instance_test + +import ( +	"context" +	"fmt" +	"io" +	"net/http" +	"net/http/httptest" +	"testing" + +	"github.com/gin-gonic/gin" +	"github.com/stretchr/testify/suite" +	"github.com/superseriousbusiness/gotosocial/internal/api/client/instance" +	"github.com/superseriousbusiness/gotosocial/internal/config" +	"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" +	"github.com/superseriousbusiness/gotosocial/testrig" +) + +type InstancePeersGetTestSuite struct { +	InstanceStandardTestSuite +} + +func (suite *InstancePeersGetTestSuite) TestInstancePeersGetNoParams() { +	recorder := httptest.NewRecorder() +	ctx, _ := gin.CreateTestContext(recorder) + +	baseURI := fmt.Sprintf("%s://%s", config.GetProtocol(), config.GetHost()) +	requestURI := fmt.Sprintf("%s/%s", baseURI, instance.InstancePeersPath) +	ctx.Request = httptest.NewRequest(http.MethodGet, requestURI, nil) + +	suite.instanceModule.InstancePeersGETHandler(ctx) + +	suite.Equal(http.StatusOK, recorder.Code) + +	result := recorder.Result() +	defer result.Body.Close() + +	b, err := io.ReadAll(result.Body) +	suite.NoError(err) + +	suite.Equal(`["example.org","fossbros-anonymous.io"]`, string(b)) +} + +func (suite *InstancePeersGetTestSuite) TestInstancePeersGetNoParamsUnauthorized() { +	config.SetInstanceExposePeers(false) + +	recorder := httptest.NewRecorder() +	ctx, _ := gin.CreateTestContext(recorder) + +	baseURI := fmt.Sprintf("%s://%s", config.GetProtocol(), config.GetHost()) +	requestURI := fmt.Sprintf("%s/%s", baseURI, instance.InstancePeersPath) +	ctx.Request = httptest.NewRequest(http.MethodGet, requestURI, nil) + +	suite.instanceModule.InstancePeersGETHandler(ctx) + +	suite.Equal(http.StatusUnauthorized, recorder.Code) + +	result := recorder.Result() +	defer result.Body.Close() + +	b, err := io.ReadAll(result.Body) +	suite.NoError(err) + +	suite.Equal(`{"error":"Unauthorized: peers open query requires an authenticated account/user"}`, string(b)) +} + +func (suite *InstancePeersGetTestSuite) TestInstancePeersGetNoParamsAuthorized() { +	config.SetInstanceExposePeers(false) + +	recorder := httptest.NewRecorder() +	baseURI := fmt.Sprintf("%s://%s", config.GetProtocol(), config.GetHost()) +	requestURI := fmt.Sprintf("%s/%s", baseURI, instance.InstancePeersPath) +	ctx := suite.newContext(recorder, http.MethodGet, []byte{}, requestURI, "") + +	suite.instanceModule.InstancePeersGETHandler(ctx) + +	suite.Equal(http.StatusOK, recorder.Code) + +	result := recorder.Result() +	defer result.Body.Close() + +	b, err := io.ReadAll(result.Body) +	suite.NoError(err) + +	suite.Equal(`["example.org","fossbros-anonymous.io"]`, string(b)) +} + +func (suite *InstancePeersGetTestSuite) TestInstancePeersGetOnlySuspended() { +	recorder := httptest.NewRecorder() +	ctx, _ := gin.CreateTestContext(recorder) + +	baseURI := fmt.Sprintf("%s://%s", config.GetProtocol(), config.GetHost()) +	requestURI := fmt.Sprintf("%s/%s?filter=suspended", baseURI, instance.InstancePeersPath) +	ctx.Request = httptest.NewRequest(http.MethodGet, requestURI, nil) + +	suite.instanceModule.InstancePeersGETHandler(ctx) + +	suite.Equal(http.StatusOK, recorder.Code) + +	result := recorder.Result() +	defer result.Body.Close() + +	b, err := io.ReadAll(result.Body) +	suite.NoError(err) + +	suite.Equal(`[{"domain":"replyguys.com","suspended_at":"2020-05-13T13:29:12.000Z","public_comment":"reply-guying to tech posts"}]`, string(b)) +} + +func (suite *InstancePeersGetTestSuite) TestInstancePeersGetOnlySuspendedUnauthorized() { +	config.SetInstanceExposeSuspended(false) + +	recorder := httptest.NewRecorder() +	ctx, _ := gin.CreateTestContext(recorder) + +	baseURI := fmt.Sprintf("%s://%s", config.GetProtocol(), config.GetHost()) +	requestURI := fmt.Sprintf("%s/%s?filter=suspended", baseURI, instance.InstancePeersPath) +	ctx.Request = httptest.NewRequest(http.MethodGet, requestURI, nil) + +	suite.instanceModule.InstancePeersGETHandler(ctx) + +	suite.Equal(http.StatusUnauthorized, recorder.Code) + +	result := recorder.Result() +	defer result.Body.Close() + +	b, err := io.ReadAll(result.Body) +	suite.NoError(err) + +	suite.Equal(`{"error":"Unauthorized: peers suspended query requires an authenticated account/user"}`, string(b)) +} + +func (suite *InstancePeersGetTestSuite) TestInstancePeersGetOnlySuspendedAuthorized() { +	config.SetInstanceExposeSuspended(false) + +	recorder := httptest.NewRecorder() +	baseURI := fmt.Sprintf("%s://%s", config.GetProtocol(), config.GetHost()) +	requestURI := fmt.Sprintf("%s/%s?filter=suspended", baseURI, instance.InstancePeersPath) +	ctx := suite.newContext(recorder, http.MethodGet, []byte{}, requestURI, "") + +	suite.instanceModule.InstancePeersGETHandler(ctx) + +	suite.Equal(http.StatusOK, recorder.Code) + +	result := recorder.Result() +	defer result.Body.Close() + +	b, err := io.ReadAll(result.Body) +	suite.NoError(err) + +	suite.Equal(`[{"domain":"replyguys.com","suspended_at":"2020-05-13T13:29:12.000Z","public_comment":"reply-guying to tech posts"}]`, string(b)) +} + +func (suite *InstancePeersGetTestSuite) TestInstancePeersGetAll() { +	recorder := httptest.NewRecorder() +	ctx, _ := gin.CreateTestContext(recorder) + +	baseURI := fmt.Sprintf("%s://%s", config.GetProtocol(), config.GetHost()) +	requestURI := fmt.Sprintf("%s/%s?filter=suspended,open", baseURI, instance.InstancePeersPath) +	ctx.Request = httptest.NewRequest(http.MethodGet, requestURI, nil) + +	suite.instanceModule.InstancePeersGETHandler(ctx) + +	suite.Equal(http.StatusOK, recorder.Code) + +	result := recorder.Result() +	defer result.Body.Close() + +	b, err := io.ReadAll(result.Body) +	suite.NoError(err) + +	suite.Equal(`[{"domain":"example.org"},{"domain":"fossbros-anonymous.io"},{"domain":"replyguys.com","suspended_at":"2020-05-13T13:29:12.000Z","public_comment":"reply-guying to tech posts"}]`, string(b)) +} + +func (suite *InstancePeersGetTestSuite) TestInstancePeersGetAllWithObfuscated() { +	err := suite.db.Put(context.Background(), >smodel.DomainBlock{ +		ID:                 "01G633XTNK51GBADQZFZQDP6WR", +		CreatedAt:          testrig.TimeMustParse("2021-06-09T12:34:55+02:00"), +		UpdatedAt:          testrig.TimeMustParse("2021-06-09T12:34:55+02:00"), +		Domain:             "omg.just.the.worst.org.ever", +		CreatedByAccountID: "01F8MH17FWEB39HZJ76B6VXSKF", +		PublicComment:      "just absolutely the worst, wowza", +		Obfuscate:          true, +	}) +	suite.NoError(err) + +	recorder := httptest.NewRecorder() +	ctx, _ := gin.CreateTestContext(recorder) + +	baseURI := fmt.Sprintf("%s://%s", config.GetProtocol(), config.GetHost()) +	requestURI := fmt.Sprintf("%s/%s?filter=suspended,open", baseURI, instance.InstancePeersPath) +	ctx.Request = httptest.NewRequest(http.MethodGet, requestURI, nil) + +	suite.instanceModule.InstancePeersGETHandler(ctx) + +	suite.Equal(http.StatusOK, recorder.Code) + +	result := recorder.Result() +	defer result.Body.Close() + +	b, err := io.ReadAll(result.Body) +	suite.NoError(err) + +	suite.Equal(`[{"domain":"example.org"},{"domain":"fossbros-anonymous.io"},{"domain":"o*g.*u**.t**.*or*t.*r**ev**","suspended_at":"2021-06-09T10:34:55.000Z","public_comment":"just absolutely the worst, wowza"},{"domain":"replyguys.com","suspended_at":"2020-05-13T13:29:12.000Z","public_comment":"reply-guying to tech posts"}]`, string(b)) +} + +func (suite *InstancePeersGetTestSuite) TestInstancePeersGetFunkyParams() { +	recorder := httptest.NewRecorder() +	ctx, _ := gin.CreateTestContext(recorder) + +	baseURI := fmt.Sprintf("%s://%s", config.GetProtocol(), config.GetHost()) +	requestURI := fmt.Sprintf("%s/%s?filter=aaaaaaaaaaaaaaaaa,open", baseURI, instance.InstancePeersPath) +	ctx.Request = httptest.NewRequest(http.MethodGet, requestURI, nil) + +	suite.instanceModule.InstancePeersGETHandler(ctx) + +	suite.Equal(http.StatusBadRequest, recorder.Code) + +	result := recorder.Result() +	defer result.Body.Close() + +	b, err := io.ReadAll(result.Body) +	suite.NoError(err) + +	suite.Equal(`{"error":"Bad Request: filter aaaaaaaaaaaaaaaaa not recognized; accepted values are 'open', 'suspended'"}`, string(b)) +} + +func TestInstancePeersGetTestSuite(t *testing.T) { +	suite.Run(t, &InstancePeersGetTestSuite{}) +} diff --git a/internal/api/model/domainblock.go b/internal/api/model/domain.go index 2911dfbaa..90c08fa6f 100644 --- a/internal/api/model/domainblock.go +++ b/internal/api/model/domain.go @@ -20,17 +20,33 @@ package model  import "mime/multipart" +// Domain represents a remote domain +// +// swagger:model domain +type Domain struct { +	// The hostname of the domain. +	// example: example.org +	Domain string `form:"domain" json:"domain" validate:"required"` +	// Time at which this domain was suspended. Key will not be present on open domains. +	// example: 2021-07-30T09:20:25+00:00 +	SuspendedAt string `json:"suspended_at,omitempty"` +	// Time at which this domain was silenced. Key will not be present on open domains. +	// example: 2021-07-30T09:20:25+00:00 +	SilencedAt string `json:"silenced_at,omitempty"` +	// If the domain is blocked, what's the publicly-stated reason for the block. +	// example: they smell +	PublicComment string `form:"public_comment" json:"public_comment,omitempty"` +} +  // DomainBlock represents a block on one domain  //  // swagger:model domainBlock  type DomainBlock struct { +	Domain  	// The ID of the domain block.  	// example: 01FBW21XJA09XYX51KV5JVBW0F  	// readonly: true  	ID string `json:"id,omitempty"` -	// The hostname of the blocked domain. -	// example: example.org -	Domain string `form:"domain" json:"domain" validation:"required"`  	// Obfuscate the domain name when serving this domain block publicly.  	// A useful anti-harassment tool.  	// example: false @@ -38,9 +54,6 @@ type DomainBlock struct {  	// Private comment for this block, visible to our instance admins only.  	// example: they are poopoo  	PrivateComment string `json:"private_comment,omitempty"` -	// Public comment for this block, visible if domain blocks are served publicly. -	// example: they smell -	PublicComment string `form:"public_comment" json:"public_comment,omitempty"`  	// The ID of the subscription that created/caused this domain block.  	// example: 01FBW25TF5J67JW3HFHZCSD23K  	SubscriptionID string `json:"subscription_id,omitempty"`  | 
