diff options
Diffstat (limited to 'internal/api/client/search')
| -rw-r--r-- | internal/api/client/search/search.go | 7 | ||||
| -rw-r--r-- | internal/api/client/search/searchget.go | 32 | ||||
| -rw-r--r-- | internal/api/client/search/searchget_test.go | 195 |
3 files changed, 222 insertions, 12 deletions
diff --git a/internal/api/client/search/search.go b/internal/api/client/search/search.go index 219e30280..d413aff91 100644 --- a/internal/api/client/search/search.go +++ b/internal/api/client/search/search.go @@ -21,12 +21,12 @@ import ( "net/http" "github.com/gin-gonic/gin" + apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util" "github.com/superseriousbusiness/gotosocial/internal/processing" ) const ( - BasePathV1 = "/v1/search" // Base path for serving v1 of the search API, minus the 'api' prefix. - BasePathV2 = "/v2/search" // Base path for serving v2 of the search API, minus the 'api' prefix. + BasePath = "/:" + apiutil.APIVersionKey + "/search" ) type Module struct { @@ -40,6 +40,5 @@ func New(processor *processing.Processor) *Module { } func (m *Module) Route(attachHandler func(method string, path string, f ...gin.HandlerFunc) gin.IRoutes) { - attachHandler(http.MethodGet, BasePathV1, m.SearchGETHandler) - attachHandler(http.MethodGet, BasePathV2, m.SearchGETHandler) + attachHandler(http.MethodGet, BasePath, m.SearchGETHandler) } diff --git a/internal/api/client/search/searchget.go b/internal/api/client/search/searchget.go index 33a90e078..2759feb5b 100644 --- a/internal/api/client/search/searchget.go +++ b/internal/api/client/search/searchget.go @@ -27,7 +27,7 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/oauth" ) -// SearchGETHandler swagger:operation GET /api/v1/search searchGet +// SearchGETHandler swagger:operation GET /api/{api_version}/search searchGet // // Search for statuses, accounts, or hashtags, on this instance or elsewhere. // @@ -42,6 +42,15 @@ import ( // // parameters: // - +// name: api_version +// type: string +// in: path +// description: >- +// Version of the API to use. Must be either `v1` or `v2`. +// If v1 is used, Hashtag results will be a slice of strings. +// If v2 is used, Hashtag results will be a slice of apimodel tags. +// required: true +// - // name: max_id // type: string // description: >- @@ -88,6 +97,7 @@ import ( // - `@[username]` -- search for an account with the given username on any domain. Can return multiple results. // - @[username]@[domain]` -- search for a remote account with exact username and domain. Will only ever return 1 result at most. // - `https://example.org/some/arbitrary/url` -- search for an account OR a status with the given URL. Will only ever return 1 result at most. +// - `#[hashtag_name]` -- search for a hashtag with the given hashtag name, or starting with the given hashtag name. Case insensitive. Can return multiple results. // - any arbitrary string -- search for accounts or statuses containing the given string. Can return multiple results. // in: query // required: true @@ -97,9 +107,9 @@ import ( // description: |- // Type of item to return. One of: // - `` -- empty string; return any/all results. -// - `accounts` -- return account(s). -// - `statuses` -- return status(es). -// - `hashtags` -- return hashtag(s). +// - `accounts` -- return only account(s). +// - `statuses` -- return only status(es). +// - `hashtags` -- return only hashtag(s). // If `type` is specified, paging can be performed using max_id and min_id parameters. // If `type` is not specified, see the `offset` parameter for paging. // in: query @@ -138,9 +148,7 @@ import ( // name: search results // description: Results of the search. // schema: -// type: array -// items: -// "$ref": "#/definitions/searchResult" +// "$ref": "#/definitions/searchResult" // '400': // description: bad request // '401': @@ -152,6 +160,15 @@ import ( // '500': // description: internal server error func (m *Module) SearchGETHandler(c *gin.Context) { + apiVersion, errWithCode := apiutil.ParseAPIVersion( + c.Param(apiutil.APIVersionKey), + []string{apiutil.APIv1, apiutil.APIv2}..., + ) + if errWithCode != nil { + apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1) + return + } + authed, err := oauth.Authed(c, true, true, true, true) if err != nil { apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGetV1) @@ -209,6 +226,7 @@ func (m *Module) SearchGETHandler(c *gin.Context) { Resolve: resolve, Following: following, ExcludeUnreviewed: excludeUnreviewed, + APIv1: apiVersion == apiutil.APIv1, } results, errWithCode := m.processor.Search().Get(c.Request.Context(), authed.Account, searchRequest) diff --git a/internal/api/client/search/searchget_test.go b/internal/api/client/search/searchget_test.go index f6a2db70a..edaac2fc1 100644 --- a/internal/api/client/search/searchget_test.go +++ b/internal/api/client/search/searchget_test.go @@ -47,6 +47,7 @@ type SearchGetTestSuite struct { func (suite *SearchGetTestSuite) getSearch( requestingAccount *gtsmodel.Account, token *gtsmodel.Token, + apiVersion string, user *gtsmodel.User, maxID *string, minID *string, @@ -62,11 +63,13 @@ func (suite *SearchGetTestSuite) getSearch( var ( recorder = httptest.NewRecorder() ctx, _ = testrig.CreateGinTestContext(recorder, nil) - requestURL = testrig.URLMustParse("/api" + search.BasePathV1) + requestURL = testrig.URLMustParse("/api" + search.BasePath) queryParts []string ) // Put the request together. + ctx.AddParam(apiutil.APIVersionKey, apiVersion) + if maxID != nil { queryParts = append(queryParts, apiutil.MaxIDKey+"="+url.QueryEscape(*maxID)) } @@ -175,6 +178,7 @@ func (suite *SearchGetTestSuite) TestSearchRemoteAccountByURI() { searchResult, err := suite.getSearch( requestingAccount, token, + apiutil.APIv2, user, maxID, minID, @@ -218,6 +222,7 @@ func (suite *SearchGetTestSuite) TestSearchRemoteAccountByNamestring() { searchResult, err := suite.getSearch( requestingAccount, token, + apiutil.APIv2, user, maxID, minID, @@ -261,6 +266,7 @@ func (suite *SearchGetTestSuite) TestSearchRemoteAccountByNamestringUppercase() searchResult, err := suite.getSearch( requestingAccount, token, + apiutil.APIv2, user, maxID, minID, @@ -304,6 +310,7 @@ func (suite *SearchGetTestSuite) TestSearchRemoteAccountByNamestringNoLeadingAt( searchResult, err := suite.getSearch( requestingAccount, token, + apiutil.APIv2, user, maxID, minID, @@ -347,6 +354,7 @@ func (suite *SearchGetTestSuite) TestSearchRemoteAccountByNamestringNoResolve() searchResult, err := suite.getSearch( requestingAccount, token, + apiutil.APIv2, user, maxID, minID, @@ -385,6 +393,7 @@ func (suite *SearchGetTestSuite) TestSearchRemoteAccountByNamestringSpecialChars searchResult, err := suite.getSearch( requestingAccount, token, + apiutil.APIv2, user, maxID, minID, @@ -426,6 +435,7 @@ func (suite *SearchGetTestSuite) TestSearchRemoteAccountByNamestringSpecialChars searchResult, err := suite.getSearch( requestingAccount, token, + apiutil.APIv2, user, maxID, minID, @@ -467,6 +477,7 @@ func (suite *SearchGetTestSuite) TestSearchLocalAccountByNamestring() { searchResult, err := suite.getSearch( requestingAccount, token, + apiutil.APIv2, user, maxID, minID, @@ -510,6 +521,7 @@ func (suite *SearchGetTestSuite) TestSearchLocalAccountByNamestringWithDomain() searchResult, err := suite.getSearch( requestingAccount, token, + apiutil.APIv2, user, maxID, minID, @@ -553,6 +565,7 @@ func (suite *SearchGetTestSuite) TestSearchNonexistingLocalAccountByNamestringRe searchResult, err := suite.getSearch( requestingAccount, token, + apiutil.APIv2, user, maxID, minID, @@ -591,6 +604,7 @@ func (suite *SearchGetTestSuite) TestSearchLocalAccountByURI() { searchResult, err := suite.getSearch( requestingAccount, token, + apiutil.APIv2, user, maxID, minID, @@ -634,6 +648,7 @@ func (suite *SearchGetTestSuite) TestSearchLocalAccountByURL() { searchResult, err := suite.getSearch( requestingAccount, token, + apiutil.APIv2, user, maxID, minID, @@ -677,6 +692,7 @@ func (suite *SearchGetTestSuite) TestSearchNonexistingLocalAccountByURL() { searchResult, err := suite.getSearch( requestingAccount, token, + apiutil.APIv2, user, maxID, minID, @@ -715,6 +731,7 @@ func (suite *SearchGetTestSuite) TestSearchStatusByURL() { searchResult, err := suite.getSearch( requestingAccount, token, + apiutil.APIv2, user, maxID, minID, @@ -758,6 +775,7 @@ func (suite *SearchGetTestSuite) TestSearchBlockedDomainURL() { searchResult, err := suite.getSearch( requestingAccount, token, + apiutil.APIv2, user, maxID, minID, @@ -798,6 +816,7 @@ func (suite *SearchGetTestSuite) TestSearchBlockedDomainNamestring() { searchResult, err := suite.getSearch( requestingAccount, token, + apiutil.APIv2, user, maxID, minID, @@ -838,6 +857,7 @@ func (suite *SearchGetTestSuite) TestSearchAAny() { searchResult, err := suite.getSearch( requestingAccount, token, + apiutil.APIv2, user, maxID, minID, @@ -878,6 +898,7 @@ func (suite *SearchGetTestSuite) TestSearchAAnyFollowingOnly() { searchResult, err := suite.getSearch( requestingAccount, token, + apiutil.APIv2, user, maxID, minID, @@ -918,6 +939,7 @@ func (suite *SearchGetTestSuite) TestSearchAStatuses() { searchResult, err := suite.getSearch( requestingAccount, token, + apiutil.APIv2, user, maxID, minID, @@ -958,6 +980,7 @@ func (suite *SearchGetTestSuite) TestSearchAAccounts() { searchResult, err := suite.getSearch( requestingAccount, token, + apiutil.APIv2, user, maxID, minID, @@ -998,6 +1021,7 @@ func (suite *SearchGetTestSuite) TestSearchAAccountsLimit1() { searchResult, err := suite.getSearch( requestingAccount, token, + apiutil.APIv2, user, maxID, minID, @@ -1038,6 +1062,7 @@ func (suite *SearchGetTestSuite) TestSearchLocalInstanceAccountByURI() { searchResult, err := suite.getSearch( requestingAccount, token, + apiutil.APIv2, user, maxID, minID, @@ -1084,6 +1109,7 @@ func (suite *SearchGetTestSuite) TestSearchInstanceAccountFull() { searchResult, err := suite.getSearch( requestingAccount, token, + apiutil.APIv2, user, maxID, minID, @@ -1130,6 +1156,7 @@ func (suite *SearchGetTestSuite) TestSearchInstanceAccountPartial() { searchResult, err := suite.getSearch( requestingAccount, token, + apiutil.APIv2, user, maxID, minID, @@ -1170,6 +1197,7 @@ func (suite *SearchGetTestSuite) TestSearchBadQueryType() { _, err := suite.getSearch( requestingAccount, token, + apiutil.APIv2, user, maxID, minID, @@ -1206,6 +1234,85 @@ func (suite *SearchGetTestSuite) TestSearchEmptyQuery() { _, err := suite.getSearch( requestingAccount, token, + apiutil.APIv2, + user, + maxID, + minID, + limit, + offset, + query, + queryType, + resolve, + following, + expectedHTTPStatus, + expectedBody) + if err != nil { + suite.FailNow(err.Error()) + } +} + +func (suite *SearchGetTestSuite) TestSearchHashtagV1() { + var ( + requestingAccount = suite.testAccounts["local_account_1"] + token = suite.testTokens["local_account_1"] + user = suite.testUsers["local_account_1"] + maxID *string = nil + minID *string = nil + limit *int = nil + offset *int = nil + resolve *bool = nil + query = "#welcome" + queryType *string = func() *string { i := "hashtags"; return &i }() + following *bool = nil + expectedHTTPStatus = http.StatusOK + expectedBody = `{"accounts":[],"statuses":[],"hashtags":[{"name":"welcome","url":"http://localhost:8080/tags/welcome","history":[]}]}` + ) + + searchResult, err := suite.getSearch( + requestingAccount, + token, + apiutil.APIv2, + user, + maxID, + minID, + limit, + offset, + query, + queryType, + resolve, + following, + expectedHTTPStatus, + expectedBody) + if err != nil { + suite.FailNow(err.Error()) + } + + suite.Len(searchResult.Accounts, 0) + suite.Len(searchResult.Statuses, 0) + suite.Len(searchResult.Hashtags, 1) +} + +func (suite *SearchGetTestSuite) TestSearchHashtagV2() { + var ( + requestingAccount = suite.testAccounts["local_account_1"] + token = suite.testTokens["local_account_1"] + user = suite.testUsers["local_account_1"] + maxID *string = nil + minID *string = nil + limit *int = nil + offset *int = nil + resolve *bool = nil + query = "#welcome" + queryType *string = func() *string { i := "hashtags"; return &i }() + following *bool = nil + expectedHTTPStatus = http.StatusOK + expectedBody = `{"accounts":[],"statuses":[],"hashtags":["welcome"]}` + ) + + searchResult, err := suite.getSearch( + requestingAccount, + token, + apiutil.APIv1, user, maxID, minID, @@ -1220,6 +1327,92 @@ func (suite *SearchGetTestSuite) TestSearchEmptyQuery() { if err != nil { suite.FailNow(err.Error()) } + + suite.Len(searchResult.Accounts, 0) + suite.Len(searchResult.Statuses, 0) + suite.Len(searchResult.Hashtags, 1) +} + +func (suite *SearchGetTestSuite) TestSearchHashtagButWithAccountSearch() { + var ( + requestingAccount = suite.testAccounts["local_account_1"] + token = suite.testTokens["local_account_1"] + user = suite.testUsers["local_account_1"] + maxID *string = nil + minID *string = nil + limit *int = nil + offset *int = nil + resolve *bool = nil + query = "#welcome" + queryType *string = func() *string { i := "accounts"; return &i }() + following *bool = nil + expectedHTTPStatus = http.StatusOK + expectedBody = `` + ) + + searchResult, err := suite.getSearch( + requestingAccount, + token, + apiutil.APIv2, + user, + maxID, + minID, + limit, + offset, + query, + queryType, + resolve, + following, + expectedHTTPStatus, + expectedBody) + if err != nil { + suite.FailNow(err.Error()) + } + + suite.Len(searchResult.Accounts, 0) + suite.Len(searchResult.Statuses, 0) + suite.Len(searchResult.Hashtags, 0) +} + +func (suite *SearchGetTestSuite) TestSearchNotHashtagButWithTypeHashtag() { + var ( + requestingAccount = suite.testAccounts["local_account_1"] + token = suite.testTokens["local_account_1"] + user = suite.testUsers["local_account_1"] + maxID *string = nil + minID *string = nil + limit *int = nil + offset *int = nil + resolve *bool = nil + query = "welco" + queryType *string = func() *string { i := "hashtags"; return &i }() + following *bool = nil + expectedHTTPStatus = http.StatusOK + expectedBody = `` + ) + + searchResult, err := suite.getSearch( + requestingAccount, + token, + apiutil.APIv2, + user, + maxID, + minID, + limit, + offset, + query, + queryType, + resolve, + following, + expectedHTTPStatus, + expectedBody) + if err != nil { + suite.FailNow(err.Error()) + } + + suite.Len(searchResult.Accounts, 0) + suite.Len(searchResult.Statuses, 0) + suite.Len(searchResult.Hashtags, 1) } func TestSearchGetTestSuite(t *testing.T) { |
