mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2024-10-31 22:40:01 +00:00
[feature] Add from: search operator and account_id query param (#2943)
* Add from: search operator * Fix whitespace in Swagger YAML comment * Move query parsing into its own method * Document search * Clarify post search scope
This commit is contained in:
parent
61a8d36255
commit
04bcde08a1
11 changed files with 312 additions and 15 deletions
|
@ -2872,6 +2872,9 @@ paths:
|
||||||
- `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.
|
- `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.
|
- `#[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.
|
- any arbitrary string -- search for accounts or statuses containing the given string. Can return multiple results.
|
||||||
|
|
||||||
|
Arbitrary string queries may include the following operators:
|
||||||
|
- `from:localuser`, `from:remoteuser@instance.tld`: restrict results to statuses created by the specified account.
|
||||||
in: query
|
in: query
|
||||||
name: q
|
name: q
|
||||||
required: true
|
required: true
|
||||||
|
@ -2902,6 +2905,10 @@ paths:
|
||||||
in: query
|
in: query
|
||||||
name: exclude_unreviewed
|
name: exclude_unreviewed
|
||||||
type: boolean
|
type: boolean
|
||||||
|
- description: Restrict results to statuses created by the specified account.
|
||||||
|
in: query
|
||||||
|
name: account_id
|
||||||
|
type: string
|
||||||
produces:
|
produces:
|
||||||
- application/json
|
- application/json
|
||||||
responses:
|
responses:
|
||||||
|
|
20
docs/user_guide/search.md
Normal file
20
docs/user_guide/search.md
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
# Search
|
||||||
|
|
||||||
|
## Query formats
|
||||||
|
|
||||||
|
GotoSocial accepts several kinds of search query:
|
||||||
|
|
||||||
|
- `@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 post with the given URL. If the account or post hasn't already federated to GotoSocial, it will try to retrieve it. 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 text`: search for posts containing the text, hashtags containing the text, and accounts with usernames, display names, or bios containing the text, exactly as written. Both posts you've written as well as posts replying to you will be searched. Account bios will only be searched for accounts that you follow. Can return multiple results.
|
||||||
|
|
||||||
|
## Search operators
|
||||||
|
|
||||||
|
Arbitrary text queries may include the following search operators:
|
||||||
|
|
||||||
|
- `from:username`: restrict results to statuses created by the specified *local* account.
|
||||||
|
- `from:username@domain`: restrict results to statuses created by the specified remote account.
|
||||||
|
|
||||||
|
For example, you can search for `sloth from:yourusername` to find your own posts about sloths.
|
|
@ -99,6 +99,9 @@
|
||||||
// - `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.
|
// - `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.
|
// - `#[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.
|
// - any arbitrary string -- search for accounts or statuses containing the given string. Can return multiple results.
|
||||||
|
//
|
||||||
|
// Arbitrary string queries may include the following operators:
|
||||||
|
// - `from:localuser`, `from:remoteuser@instance.tld`: restrict results to statuses created by the specified account.
|
||||||
// in: query
|
// in: query
|
||||||
// required: true
|
// required: true
|
||||||
// -
|
// -
|
||||||
|
@ -138,6 +141,12 @@
|
||||||
// Currently this parameter is unused.
|
// Currently this parameter is unused.
|
||||||
// default: false
|
// default: false
|
||||||
// in: query
|
// in: query
|
||||||
|
// -
|
||||||
|
// name: account_id
|
||||||
|
// type: string
|
||||||
|
// description: >-
|
||||||
|
// Restrict results to statuses created by the specified account.
|
||||||
|
// in: query
|
||||||
//
|
//
|
||||||
// security:
|
// security:
|
||||||
// - OAuth2 Bearer:
|
// - OAuth2 Bearer:
|
||||||
|
@ -238,6 +247,7 @@ func (m *Module) SearchGETHandler(c *gin.Context) {
|
||||||
Resolve: resolve,
|
Resolve: resolve,
|
||||||
Following: following,
|
Following: following,
|
||||||
ExcludeUnreviewed: excludeUnreviewed,
|
ExcludeUnreviewed: excludeUnreviewed,
|
||||||
|
AccountID: c.Query(apiutil.SearchAccountIDKey),
|
||||||
APIv1: apiVersion == apiutil.APIv1,
|
APIv1: apiVersion == apiutil.APIv1,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -60,6 +60,7 @@ func (suite *SearchGetTestSuite) getSearch(
|
||||||
queryType *string,
|
queryType *string,
|
||||||
resolve *bool,
|
resolve *bool,
|
||||||
following *bool,
|
following *bool,
|
||||||
|
fromAccountID *string,
|
||||||
expectedHTTPStatus int,
|
expectedHTTPStatus int,
|
||||||
expectedBody string,
|
expectedBody string,
|
||||||
) (*apimodel.SearchResult, error) {
|
) (*apimodel.SearchResult, error) {
|
||||||
|
@ -103,6 +104,10 @@ func (suite *SearchGetTestSuite) getSearch(
|
||||||
queryParts = append(queryParts, apiutil.SearchFollowingKey+"="+strconv.FormatBool(*following))
|
queryParts = append(queryParts, apiutil.SearchFollowingKey+"="+strconv.FormatBool(*following))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if fromAccountID != nil {
|
||||||
|
queryParts = append(queryParts, apiutil.SearchAccountIDKey+"="+url.QueryEscape(*fromAccountID))
|
||||||
|
}
|
||||||
|
|
||||||
requestURL.RawQuery = strings.Join(queryParts, "&")
|
requestURL.RawQuery = strings.Join(queryParts, "&")
|
||||||
ctx.Request = httptest.NewRequest(http.MethodGet, requestURL.String(), nil)
|
ctx.Request = httptest.NewRequest(http.MethodGet, requestURL.String(), nil)
|
||||||
ctx.Set(oauth.SessionAuthorizedAccount, requestingAccount)
|
ctx.Set(oauth.SessionAuthorizedAccount, requestingAccount)
|
||||||
|
@ -174,6 +179,7 @@ func (suite *SearchGetTestSuite) TestSearchRemoteAccountByURI() {
|
||||||
query = "https://unknown-instance.com/users/brand_new_person"
|
query = "https://unknown-instance.com/users/brand_new_person"
|
||||||
queryType *string = func() *string { i := "accounts"; return &i }()
|
queryType *string = func() *string { i := "accounts"; return &i }()
|
||||||
following *bool = nil
|
following *bool = nil
|
||||||
|
fromAccountID *string = nil
|
||||||
expectedHTTPStatus = http.StatusOK
|
expectedHTTPStatus = http.StatusOK
|
||||||
expectedBody = ""
|
expectedBody = ""
|
||||||
)
|
)
|
||||||
|
@ -191,6 +197,7 @@ func (suite *SearchGetTestSuite) TestSearchRemoteAccountByURI() {
|
||||||
queryType,
|
queryType,
|
||||||
resolve,
|
resolve,
|
||||||
following,
|
following,
|
||||||
|
fromAccountID,
|
||||||
expectedHTTPStatus,
|
expectedHTTPStatus,
|
||||||
expectedBody)
|
expectedBody)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -218,6 +225,7 @@ func (suite *SearchGetTestSuite) TestSearchRemoteAccountByNamestring() {
|
||||||
query = "@brand_new_person@unknown-instance.com"
|
query = "@brand_new_person@unknown-instance.com"
|
||||||
queryType *string = func() *string { i := "accounts"; return &i }()
|
queryType *string = func() *string { i := "accounts"; return &i }()
|
||||||
following *bool = nil
|
following *bool = nil
|
||||||
|
fromAccountID *string = nil
|
||||||
expectedHTTPStatus = http.StatusOK
|
expectedHTTPStatus = http.StatusOK
|
||||||
expectedBody = ""
|
expectedBody = ""
|
||||||
)
|
)
|
||||||
|
@ -235,6 +243,7 @@ func (suite *SearchGetTestSuite) TestSearchRemoteAccountByNamestring() {
|
||||||
queryType,
|
queryType,
|
||||||
resolve,
|
resolve,
|
||||||
following,
|
following,
|
||||||
|
fromAccountID,
|
||||||
expectedHTTPStatus,
|
expectedHTTPStatus,
|
||||||
expectedBody)
|
expectedBody)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -262,6 +271,7 @@ func (suite *SearchGetTestSuite) TestSearchRemoteAccountByNamestringUppercase()
|
||||||
query = "@Some_User@example.org"
|
query = "@Some_User@example.org"
|
||||||
queryType *string = func() *string { i := "accounts"; return &i }()
|
queryType *string = func() *string { i := "accounts"; return &i }()
|
||||||
following *bool = nil
|
following *bool = nil
|
||||||
|
fromAccountID *string = nil
|
||||||
expectedHTTPStatus = http.StatusOK
|
expectedHTTPStatus = http.StatusOK
|
||||||
expectedBody = ""
|
expectedBody = ""
|
||||||
)
|
)
|
||||||
|
@ -279,6 +289,7 @@ func (suite *SearchGetTestSuite) TestSearchRemoteAccountByNamestringUppercase()
|
||||||
queryType,
|
queryType,
|
||||||
resolve,
|
resolve,
|
||||||
following,
|
following,
|
||||||
|
fromAccountID,
|
||||||
expectedHTTPStatus,
|
expectedHTTPStatus,
|
||||||
expectedBody)
|
expectedBody)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -306,6 +317,7 @@ func (suite *SearchGetTestSuite) TestSearchRemoteAccountByNamestringNoLeadingAt(
|
||||||
query = "brand_new_person@unknown-instance.com"
|
query = "brand_new_person@unknown-instance.com"
|
||||||
queryType *string = func() *string { i := "accounts"; return &i }()
|
queryType *string = func() *string { i := "accounts"; return &i }()
|
||||||
following *bool = nil
|
following *bool = nil
|
||||||
|
fromAccountID *string = nil
|
||||||
expectedHTTPStatus = http.StatusOK
|
expectedHTTPStatus = http.StatusOK
|
||||||
expectedBody = ""
|
expectedBody = ""
|
||||||
)
|
)
|
||||||
|
@ -323,6 +335,7 @@ func (suite *SearchGetTestSuite) TestSearchRemoteAccountByNamestringNoLeadingAt(
|
||||||
queryType,
|
queryType,
|
||||||
resolve,
|
resolve,
|
||||||
following,
|
following,
|
||||||
|
fromAccountID,
|
||||||
expectedHTTPStatus,
|
expectedHTTPStatus,
|
||||||
expectedBody)
|
expectedBody)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -350,6 +363,7 @@ func (suite *SearchGetTestSuite) TestSearchRemoteAccountByNamestringNoResolve()
|
||||||
query = "@brand_new_person@unknown-instance.com"
|
query = "@brand_new_person@unknown-instance.com"
|
||||||
queryType *string = func() *string { i := "accounts"; return &i }()
|
queryType *string = func() *string { i := "accounts"; return &i }()
|
||||||
following *bool = nil
|
following *bool = nil
|
||||||
|
fromAccountID *string = nil
|
||||||
expectedHTTPStatus = http.StatusOK
|
expectedHTTPStatus = http.StatusOK
|
||||||
expectedBody = ""
|
expectedBody = ""
|
||||||
)
|
)
|
||||||
|
@ -367,6 +381,7 @@ func (suite *SearchGetTestSuite) TestSearchRemoteAccountByNamestringNoResolve()
|
||||||
queryType,
|
queryType,
|
||||||
resolve,
|
resolve,
|
||||||
following,
|
following,
|
||||||
|
fromAccountID,
|
||||||
expectedHTTPStatus,
|
expectedHTTPStatus,
|
||||||
expectedBody)
|
expectedBody)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -389,6 +404,7 @@ func (suite *SearchGetTestSuite) TestSearchRemoteAccountByNamestringSpecialChars
|
||||||
query = "@üser@ëxample.org"
|
query = "@üser@ëxample.org"
|
||||||
queryType *string = func() *string { i := "accounts"; return &i }()
|
queryType *string = func() *string { i := "accounts"; return &i }()
|
||||||
following *bool = nil
|
following *bool = nil
|
||||||
|
fromAccountID *string = nil
|
||||||
expectedHTTPStatus = http.StatusOK
|
expectedHTTPStatus = http.StatusOK
|
||||||
expectedBody = ""
|
expectedBody = ""
|
||||||
)
|
)
|
||||||
|
@ -406,6 +422,7 @@ func (suite *SearchGetTestSuite) TestSearchRemoteAccountByNamestringSpecialChars
|
||||||
queryType,
|
queryType,
|
||||||
resolve,
|
resolve,
|
||||||
following,
|
following,
|
||||||
|
fromAccountID,
|
||||||
expectedHTTPStatus,
|
expectedHTTPStatus,
|
||||||
expectedBody)
|
expectedBody)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -431,6 +448,7 @@ func (suite *SearchGetTestSuite) TestSearchRemoteAccountByNamestringSpecialChars
|
||||||
query = "@üser@xn--xample-ova.org"
|
query = "@üser@xn--xample-ova.org"
|
||||||
queryType *string = func() *string { i := "accounts"; return &i }()
|
queryType *string = func() *string { i := "accounts"; return &i }()
|
||||||
following *bool = nil
|
following *bool = nil
|
||||||
|
fromAccountID *string = nil
|
||||||
expectedHTTPStatus = http.StatusOK
|
expectedHTTPStatus = http.StatusOK
|
||||||
expectedBody = ""
|
expectedBody = ""
|
||||||
)
|
)
|
||||||
|
@ -448,6 +466,7 @@ func (suite *SearchGetTestSuite) TestSearchRemoteAccountByNamestringSpecialChars
|
||||||
queryType,
|
queryType,
|
||||||
resolve,
|
resolve,
|
||||||
following,
|
following,
|
||||||
|
fromAccountID,
|
||||||
expectedHTTPStatus,
|
expectedHTTPStatus,
|
||||||
expectedBody)
|
expectedBody)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -473,6 +492,7 @@ func (suite *SearchGetTestSuite) TestSearchLocalAccountByNamestring() {
|
||||||
query = "@the_mighty_zork"
|
query = "@the_mighty_zork"
|
||||||
queryType *string = func() *string { i := "accounts"; return &i }()
|
queryType *string = func() *string { i := "accounts"; return &i }()
|
||||||
following *bool = nil
|
following *bool = nil
|
||||||
|
fromAccountID *string = nil
|
||||||
expectedHTTPStatus = http.StatusOK
|
expectedHTTPStatus = http.StatusOK
|
||||||
expectedBody = ""
|
expectedBody = ""
|
||||||
)
|
)
|
||||||
|
@ -490,6 +510,7 @@ func (suite *SearchGetTestSuite) TestSearchLocalAccountByNamestring() {
|
||||||
queryType,
|
queryType,
|
||||||
resolve,
|
resolve,
|
||||||
following,
|
following,
|
||||||
|
fromAccountID,
|
||||||
expectedHTTPStatus,
|
expectedHTTPStatus,
|
||||||
expectedBody)
|
expectedBody)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -517,6 +538,7 @@ func (suite *SearchGetTestSuite) TestSearchLocalAccountByNamestringWithDomain()
|
||||||
query = "@the_mighty_zork@localhost:8080"
|
query = "@the_mighty_zork@localhost:8080"
|
||||||
queryType *string = func() *string { i := "accounts"; return &i }()
|
queryType *string = func() *string { i := "accounts"; return &i }()
|
||||||
following *bool = nil
|
following *bool = nil
|
||||||
|
fromAccountID *string = nil
|
||||||
expectedHTTPStatus = http.StatusOK
|
expectedHTTPStatus = http.StatusOK
|
||||||
expectedBody = ""
|
expectedBody = ""
|
||||||
)
|
)
|
||||||
|
@ -534,6 +556,7 @@ func (suite *SearchGetTestSuite) TestSearchLocalAccountByNamestringWithDomain()
|
||||||
queryType,
|
queryType,
|
||||||
resolve,
|
resolve,
|
||||||
following,
|
following,
|
||||||
|
fromAccountID,
|
||||||
expectedHTTPStatus,
|
expectedHTTPStatus,
|
||||||
expectedBody)
|
expectedBody)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -561,6 +584,7 @@ func (suite *SearchGetTestSuite) TestSearchNonexistingLocalAccountByNamestringRe
|
||||||
query = "@somone_made_up@localhost:8080"
|
query = "@somone_made_up@localhost:8080"
|
||||||
queryType *string = func() *string { i := "accounts"; return &i }()
|
queryType *string = func() *string { i := "accounts"; return &i }()
|
||||||
following *bool = nil
|
following *bool = nil
|
||||||
|
fromAccountID *string = nil
|
||||||
expectedHTTPStatus = http.StatusOK
|
expectedHTTPStatus = http.StatusOK
|
||||||
expectedBody = ""
|
expectedBody = ""
|
||||||
)
|
)
|
||||||
|
@ -578,6 +602,7 @@ func (suite *SearchGetTestSuite) TestSearchNonexistingLocalAccountByNamestringRe
|
||||||
queryType,
|
queryType,
|
||||||
resolve,
|
resolve,
|
||||||
following,
|
following,
|
||||||
|
fromAccountID,
|
||||||
expectedHTTPStatus,
|
expectedHTTPStatus,
|
||||||
expectedBody)
|
expectedBody)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -600,6 +625,7 @@ func (suite *SearchGetTestSuite) TestSearchLocalAccountByURI() {
|
||||||
query = "http://localhost:8080/users/the_mighty_zork"
|
query = "http://localhost:8080/users/the_mighty_zork"
|
||||||
queryType *string = func() *string { i := "accounts"; return &i }()
|
queryType *string = func() *string { i := "accounts"; return &i }()
|
||||||
following *bool = nil
|
following *bool = nil
|
||||||
|
fromAccountID *string = nil
|
||||||
expectedHTTPStatus = http.StatusOK
|
expectedHTTPStatus = http.StatusOK
|
||||||
expectedBody = ""
|
expectedBody = ""
|
||||||
)
|
)
|
||||||
|
@ -617,6 +643,7 @@ func (suite *SearchGetTestSuite) TestSearchLocalAccountByURI() {
|
||||||
queryType,
|
queryType,
|
||||||
resolve,
|
resolve,
|
||||||
following,
|
following,
|
||||||
|
fromAccountID,
|
||||||
expectedHTTPStatus,
|
expectedHTTPStatus,
|
||||||
expectedBody)
|
expectedBody)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -644,6 +671,7 @@ func (suite *SearchGetTestSuite) TestSearchLocalAccountByURL() {
|
||||||
query = "http://localhost:8080/@the_mighty_zork"
|
query = "http://localhost:8080/@the_mighty_zork"
|
||||||
queryType *string = func() *string { i := "accounts"; return &i }()
|
queryType *string = func() *string { i := "accounts"; return &i }()
|
||||||
following *bool = nil
|
following *bool = nil
|
||||||
|
fromAccountID *string = nil
|
||||||
expectedHTTPStatus = http.StatusOK
|
expectedHTTPStatus = http.StatusOK
|
||||||
expectedBody = ""
|
expectedBody = ""
|
||||||
)
|
)
|
||||||
|
@ -661,6 +689,7 @@ func (suite *SearchGetTestSuite) TestSearchLocalAccountByURL() {
|
||||||
queryType,
|
queryType,
|
||||||
resolve,
|
resolve,
|
||||||
following,
|
following,
|
||||||
|
fromAccountID,
|
||||||
expectedHTTPStatus,
|
expectedHTTPStatus,
|
||||||
expectedBody)
|
expectedBody)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -688,6 +717,7 @@ func (suite *SearchGetTestSuite) TestSearchNonexistingLocalAccountByURL() {
|
||||||
query = "http://localhost:8080/@the_shmighty_shmork"
|
query = "http://localhost:8080/@the_shmighty_shmork"
|
||||||
queryType *string = func() *string { i := "accounts"; return &i }()
|
queryType *string = func() *string { i := "accounts"; return &i }()
|
||||||
following *bool = nil
|
following *bool = nil
|
||||||
|
fromAccountID *string = nil
|
||||||
expectedHTTPStatus = http.StatusOK
|
expectedHTTPStatus = http.StatusOK
|
||||||
expectedBody = ""
|
expectedBody = ""
|
||||||
)
|
)
|
||||||
|
@ -705,6 +735,7 @@ func (suite *SearchGetTestSuite) TestSearchNonexistingLocalAccountByURL() {
|
||||||
queryType,
|
queryType,
|
||||||
resolve,
|
resolve,
|
||||||
following,
|
following,
|
||||||
|
fromAccountID,
|
||||||
expectedHTTPStatus,
|
expectedHTTPStatus,
|
||||||
expectedBody)
|
expectedBody)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -727,6 +758,7 @@ func (suite *SearchGetTestSuite) TestSearchStatusByURL() {
|
||||||
query = "https://turnip.farm/users/turniplover6969/statuses/70c53e54-3146-42d5-a630-83c8b6c7c042"
|
query = "https://turnip.farm/users/turniplover6969/statuses/70c53e54-3146-42d5-a630-83c8b6c7c042"
|
||||||
queryType *string = func() *string { i := "statuses"; return &i }()
|
queryType *string = func() *string { i := "statuses"; return &i }()
|
||||||
following *bool = nil
|
following *bool = nil
|
||||||
|
fromAccountID *string = nil
|
||||||
expectedHTTPStatus = http.StatusOK
|
expectedHTTPStatus = http.StatusOK
|
||||||
expectedBody = ""
|
expectedBody = ""
|
||||||
)
|
)
|
||||||
|
@ -744,6 +776,7 @@ func (suite *SearchGetTestSuite) TestSearchStatusByURL() {
|
||||||
queryType,
|
queryType,
|
||||||
resolve,
|
resolve,
|
||||||
following,
|
following,
|
||||||
|
fromAccountID,
|
||||||
expectedHTTPStatus,
|
expectedHTTPStatus,
|
||||||
expectedBody)
|
expectedBody)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -771,6 +804,7 @@ func (suite *SearchGetTestSuite) TestSearchBlockedDomainURL() {
|
||||||
query = "https://replyguys.com/@someone"
|
query = "https://replyguys.com/@someone"
|
||||||
queryType *string = func() *string { i := "accounts"; return &i }()
|
queryType *string = func() *string { i := "accounts"; return &i }()
|
||||||
following *bool = nil
|
following *bool = nil
|
||||||
|
fromAccountID *string = nil
|
||||||
expectedHTTPStatus = http.StatusOK
|
expectedHTTPStatus = http.StatusOK
|
||||||
expectedBody = ""
|
expectedBody = ""
|
||||||
)
|
)
|
||||||
|
@ -788,6 +822,7 @@ func (suite *SearchGetTestSuite) TestSearchBlockedDomainURL() {
|
||||||
queryType,
|
queryType,
|
||||||
resolve,
|
resolve,
|
||||||
following,
|
following,
|
||||||
|
fromAccountID,
|
||||||
expectedHTTPStatus,
|
expectedHTTPStatus,
|
||||||
expectedBody)
|
expectedBody)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -812,6 +847,7 @@ func (suite *SearchGetTestSuite) TestSearchBlockedDomainNamestring() {
|
||||||
query = "@someone@replyguys.com"
|
query = "@someone@replyguys.com"
|
||||||
queryType *string = func() *string { i := "accounts"; return &i }()
|
queryType *string = func() *string { i := "accounts"; return &i }()
|
||||||
following *bool = nil
|
following *bool = nil
|
||||||
|
fromAccountID *string = nil
|
||||||
expectedHTTPStatus = http.StatusOK
|
expectedHTTPStatus = http.StatusOK
|
||||||
expectedBody = ""
|
expectedBody = ""
|
||||||
)
|
)
|
||||||
|
@ -829,6 +865,7 @@ func (suite *SearchGetTestSuite) TestSearchBlockedDomainNamestring() {
|
||||||
queryType,
|
queryType,
|
||||||
resolve,
|
resolve,
|
||||||
following,
|
following,
|
||||||
|
fromAccountID,
|
||||||
expectedHTTPStatus,
|
expectedHTTPStatus,
|
||||||
expectedBody)
|
expectedBody)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -853,6 +890,7 @@ func (suite *SearchGetTestSuite) TestSearchAAny() {
|
||||||
query = "a"
|
query = "a"
|
||||||
queryType *string = nil // Return anything.
|
queryType *string = nil // Return anything.
|
||||||
following *bool = nil
|
following *bool = nil
|
||||||
|
fromAccountID *string = nil
|
||||||
expectedHTTPStatus = http.StatusOK
|
expectedHTTPStatus = http.StatusOK
|
||||||
expectedBody = ""
|
expectedBody = ""
|
||||||
)
|
)
|
||||||
|
@ -870,6 +908,7 @@ func (suite *SearchGetTestSuite) TestSearchAAny() {
|
||||||
queryType,
|
queryType,
|
||||||
resolve,
|
resolve,
|
||||||
following,
|
following,
|
||||||
|
fromAccountID,
|
||||||
expectedHTTPStatus,
|
expectedHTTPStatus,
|
||||||
expectedBody)
|
expectedBody)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -894,6 +933,7 @@ func (suite *SearchGetTestSuite) TestSearchAAnyFollowingOnly() {
|
||||||
query = "a"
|
query = "a"
|
||||||
queryType *string = nil // Return anything.
|
queryType *string = nil // Return anything.
|
||||||
following *bool = func() *bool { i := true; return &i }()
|
following *bool = func() *bool { i := true; return &i }()
|
||||||
|
fromAccountID *string = nil
|
||||||
expectedHTTPStatus = http.StatusOK
|
expectedHTTPStatus = http.StatusOK
|
||||||
expectedBody = ""
|
expectedBody = ""
|
||||||
)
|
)
|
||||||
|
@ -911,6 +951,7 @@ func (suite *SearchGetTestSuite) TestSearchAAnyFollowingOnly() {
|
||||||
queryType,
|
queryType,
|
||||||
resolve,
|
resolve,
|
||||||
following,
|
following,
|
||||||
|
fromAccountID,
|
||||||
expectedHTTPStatus,
|
expectedHTTPStatus,
|
||||||
expectedBody)
|
expectedBody)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -935,6 +976,7 @@ func (suite *SearchGetTestSuite) TestSearchAStatuses() {
|
||||||
query = "a"
|
query = "a"
|
||||||
queryType *string = func() *string { i := "statuses"; return &i }() // Only statuses.
|
queryType *string = func() *string { i := "statuses"; return &i }() // Only statuses.
|
||||||
following *bool = nil
|
following *bool = nil
|
||||||
|
fromAccountID *string = nil
|
||||||
expectedHTTPStatus = http.StatusOK
|
expectedHTTPStatus = http.StatusOK
|
||||||
expectedBody = ""
|
expectedBody = ""
|
||||||
)
|
)
|
||||||
|
@ -952,6 +994,7 @@ func (suite *SearchGetTestSuite) TestSearchAStatuses() {
|
||||||
queryType,
|
queryType,
|
||||||
resolve,
|
resolve,
|
||||||
following,
|
following,
|
||||||
|
fromAccountID,
|
||||||
expectedHTTPStatus,
|
expectedHTTPStatus,
|
||||||
expectedBody)
|
expectedBody)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -963,6 +1006,92 @@ func (suite *SearchGetTestSuite) TestSearchAStatuses() {
|
||||||
suite.Len(searchResult.Hashtags, 0)
|
suite.Len(searchResult.Hashtags, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (suite *SearchGetTestSuite) TestSearchHiStatusesWithAccountIDInQueryParam() {
|
||||||
|
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 = func() *bool { i := true; return &i }()
|
||||||
|
query = "hi"
|
||||||
|
queryType *string = func() *string { i := "statuses"; return &i }() // Only statuses.
|
||||||
|
following *bool = nil
|
||||||
|
fromAccountID *string = func() *string { i := suite.testAccounts["local_account_2"].ID; return &i }()
|
||||||
|
expectedHTTPStatus = http.StatusOK
|
||||||
|
expectedBody = ""
|
||||||
|
)
|
||||||
|
|
||||||
|
searchResult, err := suite.getSearch(
|
||||||
|
requestingAccount,
|
||||||
|
token,
|
||||||
|
apiutil.APIv2,
|
||||||
|
user,
|
||||||
|
maxID,
|
||||||
|
minID,
|
||||||
|
limit,
|
||||||
|
offset,
|
||||||
|
query,
|
||||||
|
queryType,
|
||||||
|
resolve,
|
||||||
|
following,
|
||||||
|
fromAccountID,
|
||||||
|
expectedHTTPStatus,
|
||||||
|
expectedBody)
|
||||||
|
if err != nil {
|
||||||
|
suite.FailNow(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
suite.Len(searchResult.Accounts, 0)
|
||||||
|
suite.Len(searchResult.Statuses, 1)
|
||||||
|
suite.Len(searchResult.Hashtags, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *SearchGetTestSuite) TestSearchHiStatusesWithAccountIDInQueryText() {
|
||||||
|
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 = func() *bool { i := true; return &i }()
|
||||||
|
query = "hi from:1happyturtle"
|
||||||
|
queryType *string = func() *string { i := "statuses"; return &i }() // Only statuses.
|
||||||
|
following *bool = nil
|
||||||
|
fromAccountID *string = nil
|
||||||
|
expectedHTTPStatus = http.StatusOK
|
||||||
|
expectedBody = ""
|
||||||
|
)
|
||||||
|
|
||||||
|
searchResult, err := suite.getSearch(
|
||||||
|
requestingAccount,
|
||||||
|
token,
|
||||||
|
apiutil.APIv2,
|
||||||
|
user,
|
||||||
|
maxID,
|
||||||
|
minID,
|
||||||
|
limit,
|
||||||
|
offset,
|
||||||
|
query,
|
||||||
|
queryType,
|
||||||
|
resolve,
|
||||||
|
following,
|
||||||
|
fromAccountID,
|
||||||
|
expectedHTTPStatus,
|
||||||
|
expectedBody)
|
||||||
|
if err != nil {
|
||||||
|
suite.FailNow(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
suite.Len(searchResult.Accounts, 0)
|
||||||
|
suite.Len(searchResult.Statuses, 1)
|
||||||
|
suite.Len(searchResult.Hashtags, 0)
|
||||||
|
}
|
||||||
|
|
||||||
func (suite *SearchGetTestSuite) TestSearchAAccounts() {
|
func (suite *SearchGetTestSuite) TestSearchAAccounts() {
|
||||||
var (
|
var (
|
||||||
requestingAccount = suite.testAccounts["local_account_1"]
|
requestingAccount = suite.testAccounts["local_account_1"]
|
||||||
|
@ -976,6 +1105,7 @@ func (suite *SearchGetTestSuite) TestSearchAAccounts() {
|
||||||
query = "a"
|
query = "a"
|
||||||
queryType *string = func() *string { i := "accounts"; return &i }() // Only accounts.
|
queryType *string = func() *string { i := "accounts"; return &i }() // Only accounts.
|
||||||
following *bool = nil
|
following *bool = nil
|
||||||
|
fromAccountID *string = nil
|
||||||
expectedHTTPStatus = http.StatusOK
|
expectedHTTPStatus = http.StatusOK
|
||||||
expectedBody = ""
|
expectedBody = ""
|
||||||
)
|
)
|
||||||
|
@ -993,6 +1123,7 @@ func (suite *SearchGetTestSuite) TestSearchAAccounts() {
|
||||||
queryType,
|
queryType,
|
||||||
resolve,
|
resolve,
|
||||||
following,
|
following,
|
||||||
|
fromAccountID,
|
||||||
expectedHTTPStatus,
|
expectedHTTPStatus,
|
||||||
expectedBody)
|
expectedBody)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1017,6 +1148,7 @@ func (suite *SearchGetTestSuite) TestSearchAccountsLimit1() {
|
||||||
query = "a"
|
query = "a"
|
||||||
queryType *string = func() *string { i := "accounts"; return &i }() // Only accounts.
|
queryType *string = func() *string { i := "accounts"; return &i }() // Only accounts.
|
||||||
following *bool = nil
|
following *bool = nil
|
||||||
|
fromAccountID *string = nil
|
||||||
expectedHTTPStatus = http.StatusOK
|
expectedHTTPStatus = http.StatusOK
|
||||||
expectedBody = ""
|
expectedBody = ""
|
||||||
)
|
)
|
||||||
|
@ -1034,6 +1166,7 @@ func (suite *SearchGetTestSuite) TestSearchAccountsLimit1() {
|
||||||
queryType,
|
queryType,
|
||||||
resolve,
|
resolve,
|
||||||
following,
|
following,
|
||||||
|
fromAccountID,
|
||||||
expectedHTTPStatus,
|
expectedHTTPStatus,
|
||||||
expectedBody)
|
expectedBody)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1058,6 +1191,7 @@ func (suite *SearchGetTestSuite) TestSearchLocalInstanceAccountByURI() {
|
||||||
query = "http://localhost:8080/users/localhost:8080"
|
query = "http://localhost:8080/users/localhost:8080"
|
||||||
queryType *string = func() *string { i := "accounts"; return &i }()
|
queryType *string = func() *string { i := "accounts"; return &i }()
|
||||||
following *bool = nil
|
following *bool = nil
|
||||||
|
fromAccountID *string = nil
|
||||||
expectedHTTPStatus = http.StatusOK
|
expectedHTTPStatus = http.StatusOK
|
||||||
expectedBody = ""
|
expectedBody = ""
|
||||||
)
|
)
|
||||||
|
@ -1075,6 +1209,7 @@ func (suite *SearchGetTestSuite) TestSearchLocalInstanceAccountByURI() {
|
||||||
queryType,
|
queryType,
|
||||||
resolve,
|
resolve,
|
||||||
following,
|
following,
|
||||||
|
fromAccountID,
|
||||||
expectedHTTPStatus,
|
expectedHTTPStatus,
|
||||||
expectedBody)
|
expectedBody)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1107,6 +1242,7 @@ func (suite *SearchGetTestSuite) TestSearchLocalInstanceAccountFull() {
|
||||||
query = "@" + newDomain + "@" + newDomain
|
query = "@" + newDomain + "@" + newDomain
|
||||||
queryType *string = nil
|
queryType *string = nil
|
||||||
following *bool = nil
|
following *bool = nil
|
||||||
|
fromAccountID *string = nil
|
||||||
expectedHTTPStatus = http.StatusOK
|
expectedHTTPStatus = http.StatusOK
|
||||||
expectedBody = ""
|
expectedBody = ""
|
||||||
)
|
)
|
||||||
|
@ -1124,6 +1260,7 @@ func (suite *SearchGetTestSuite) TestSearchLocalInstanceAccountFull() {
|
||||||
queryType,
|
queryType,
|
||||||
resolve,
|
resolve,
|
||||||
following,
|
following,
|
||||||
|
fromAccountID,
|
||||||
expectedHTTPStatus,
|
expectedHTTPStatus,
|
||||||
expectedBody)
|
expectedBody)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1156,6 +1293,7 @@ func (suite *SearchGetTestSuite) TestSearchLocalInstanceAccountPartial() {
|
||||||
query = "@" + newDomain
|
query = "@" + newDomain
|
||||||
queryType *string = nil
|
queryType *string = nil
|
||||||
following *bool = nil
|
following *bool = nil
|
||||||
|
fromAccountID *string = nil
|
||||||
expectedHTTPStatus = http.StatusOK
|
expectedHTTPStatus = http.StatusOK
|
||||||
expectedBody = ""
|
expectedBody = ""
|
||||||
)
|
)
|
||||||
|
@ -1173,6 +1311,7 @@ func (suite *SearchGetTestSuite) TestSearchLocalInstanceAccountPartial() {
|
||||||
queryType,
|
queryType,
|
||||||
resolve,
|
resolve,
|
||||||
following,
|
following,
|
||||||
|
fromAccountID,
|
||||||
expectedHTTPStatus,
|
expectedHTTPStatus,
|
||||||
expectedBody)
|
expectedBody)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1206,6 +1345,7 @@ func (suite *SearchGetTestSuite) TestSearchLocalInstanceAccountEvenMorePartial()
|
||||||
query = newDomain
|
query = newDomain
|
||||||
queryType *string = nil
|
queryType *string = nil
|
||||||
following *bool = nil
|
following *bool = nil
|
||||||
|
fromAccountID *string = nil
|
||||||
expectedHTTPStatus = http.StatusOK
|
expectedHTTPStatus = http.StatusOK
|
||||||
expectedBody = ""
|
expectedBody = ""
|
||||||
)
|
)
|
||||||
|
@ -1223,6 +1363,7 @@ func (suite *SearchGetTestSuite) TestSearchLocalInstanceAccountEvenMorePartial()
|
||||||
queryType,
|
queryType,
|
||||||
resolve,
|
resolve,
|
||||||
following,
|
following,
|
||||||
|
fromAccountID,
|
||||||
expectedHTTPStatus,
|
expectedHTTPStatus,
|
||||||
expectedBody)
|
expectedBody)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1280,6 +1421,7 @@ func (suite *SearchGetTestSuite) TestSearchRemoteInstanceAccountPartial() {
|
||||||
query = "@" + theirDomain
|
query = "@" + theirDomain
|
||||||
queryType *string = nil
|
queryType *string = nil
|
||||||
following *bool = nil
|
following *bool = nil
|
||||||
|
fromAccountID *string = nil
|
||||||
expectedHTTPStatus = http.StatusOK
|
expectedHTTPStatus = http.StatusOK
|
||||||
expectedBody = ""
|
expectedBody = ""
|
||||||
)
|
)
|
||||||
|
@ -1297,6 +1439,7 @@ func (suite *SearchGetTestSuite) TestSearchRemoteInstanceAccountPartial() {
|
||||||
queryType,
|
queryType,
|
||||||
resolve,
|
resolve,
|
||||||
following,
|
following,
|
||||||
|
fromAccountID,
|
||||||
expectedHTTPStatus,
|
expectedHTTPStatus,
|
||||||
expectedBody)
|
expectedBody)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1323,6 +1466,7 @@ func (suite *SearchGetTestSuite) TestSearchBadQueryType() {
|
||||||
query = "whatever"
|
query = "whatever"
|
||||||
queryType *string = func() *string { i := "aaaaaaaaaaa"; return &i }()
|
queryType *string = func() *string { i := "aaaaaaaaaaa"; return &i }()
|
||||||
following *bool = nil
|
following *bool = nil
|
||||||
|
fromAccountID *string = nil
|
||||||
expectedHTTPStatus = http.StatusBadRequest
|
expectedHTTPStatus = http.StatusBadRequest
|
||||||
expectedBody = `{"error":"Bad Request: search query type aaaaaaaaaaa was not recognized, valid options are ['', 'accounts', 'statuses', 'hashtags']"}`
|
expectedBody = `{"error":"Bad Request: search query type aaaaaaaaaaa was not recognized, valid options are ['', 'accounts', 'statuses', 'hashtags']"}`
|
||||||
)
|
)
|
||||||
|
@ -1340,6 +1484,7 @@ func (suite *SearchGetTestSuite) TestSearchBadQueryType() {
|
||||||
queryType,
|
queryType,
|
||||||
resolve,
|
resolve,
|
||||||
following,
|
following,
|
||||||
|
fromAccountID,
|
||||||
expectedHTTPStatus,
|
expectedHTTPStatus,
|
||||||
expectedBody)
|
expectedBody)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1360,6 +1505,7 @@ func (suite *SearchGetTestSuite) TestSearchEmptyQuery() {
|
||||||
query = ""
|
query = ""
|
||||||
queryType *string = func() *string { i := "aaaaaaaaaaa"; return &i }()
|
queryType *string = func() *string { i := "aaaaaaaaaaa"; return &i }()
|
||||||
following *bool = nil
|
following *bool = nil
|
||||||
|
fromAccountID *string = nil
|
||||||
expectedHTTPStatus = http.StatusBadRequest
|
expectedHTTPStatus = http.StatusBadRequest
|
||||||
expectedBody = `{"error":"Bad Request: required key q was not set or had empty value"}`
|
expectedBody = `{"error":"Bad Request: required key q was not set or had empty value"}`
|
||||||
)
|
)
|
||||||
|
@ -1377,6 +1523,7 @@ func (suite *SearchGetTestSuite) TestSearchEmptyQuery() {
|
||||||
queryType,
|
queryType,
|
||||||
resolve,
|
resolve,
|
||||||
following,
|
following,
|
||||||
|
fromAccountID,
|
||||||
expectedHTTPStatus,
|
expectedHTTPStatus,
|
||||||
expectedBody)
|
expectedBody)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1397,6 +1544,7 @@ func (suite *SearchGetTestSuite) TestSearchHashtagV1() {
|
||||||
query = "#welcome"
|
query = "#welcome"
|
||||||
queryType *string = func() *string { i := "hashtags"; return &i }()
|
queryType *string = func() *string { i := "hashtags"; return &i }()
|
||||||
following *bool = nil
|
following *bool = nil
|
||||||
|
fromAccountID *string = nil
|
||||||
expectedHTTPStatus = http.StatusOK
|
expectedHTTPStatus = http.StatusOK
|
||||||
expectedBody = `{"accounts":[],"statuses":[],"hashtags":[{"name":"welcome","url":"http://localhost:8080/tags/welcome","history":[]}]}`
|
expectedBody = `{"accounts":[],"statuses":[],"hashtags":[{"name":"welcome","url":"http://localhost:8080/tags/welcome","history":[]}]}`
|
||||||
)
|
)
|
||||||
|
@ -1414,6 +1562,7 @@ func (suite *SearchGetTestSuite) TestSearchHashtagV1() {
|
||||||
queryType,
|
queryType,
|
||||||
resolve,
|
resolve,
|
||||||
following,
|
following,
|
||||||
|
fromAccountID,
|
||||||
expectedHTTPStatus,
|
expectedHTTPStatus,
|
||||||
expectedBody)
|
expectedBody)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1438,6 +1587,7 @@ func (suite *SearchGetTestSuite) TestSearchHashtagV2() {
|
||||||
query = "#welcome"
|
query = "#welcome"
|
||||||
queryType *string = func() *string { i := "hashtags"; return &i }()
|
queryType *string = func() *string { i := "hashtags"; return &i }()
|
||||||
following *bool = nil
|
following *bool = nil
|
||||||
|
fromAccountID *string = nil
|
||||||
expectedHTTPStatus = http.StatusOK
|
expectedHTTPStatus = http.StatusOK
|
||||||
expectedBody = `{"accounts":[],"statuses":[],"hashtags":["welcome"]}`
|
expectedBody = `{"accounts":[],"statuses":[],"hashtags":["welcome"]}`
|
||||||
)
|
)
|
||||||
|
@ -1455,6 +1605,7 @@ func (suite *SearchGetTestSuite) TestSearchHashtagV2() {
|
||||||
queryType,
|
queryType,
|
||||||
resolve,
|
resolve,
|
||||||
following,
|
following,
|
||||||
|
fromAccountID,
|
||||||
expectedHTTPStatus,
|
expectedHTTPStatus,
|
||||||
expectedBody)
|
expectedBody)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1479,6 +1630,7 @@ func (suite *SearchGetTestSuite) TestSearchHashtagButWithAccountSearch() {
|
||||||
query = "#welcome"
|
query = "#welcome"
|
||||||
queryType *string = func() *string { i := "accounts"; return &i }()
|
queryType *string = func() *string { i := "accounts"; return &i }()
|
||||||
following *bool = nil
|
following *bool = nil
|
||||||
|
fromAccountID *string = nil
|
||||||
expectedHTTPStatus = http.StatusOK
|
expectedHTTPStatus = http.StatusOK
|
||||||
expectedBody = ``
|
expectedBody = ``
|
||||||
)
|
)
|
||||||
|
@ -1496,6 +1648,7 @@ func (suite *SearchGetTestSuite) TestSearchHashtagButWithAccountSearch() {
|
||||||
queryType,
|
queryType,
|
||||||
resolve,
|
resolve,
|
||||||
following,
|
following,
|
||||||
|
fromAccountID,
|
||||||
expectedHTTPStatus,
|
expectedHTTPStatus,
|
||||||
expectedBody)
|
expectedBody)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1520,6 +1673,7 @@ func (suite *SearchGetTestSuite) TestSearchNotHashtagButWithTypeHashtag() {
|
||||||
query = "welco"
|
query = "welco"
|
||||||
queryType *string = func() *string { i := "hashtags"; return &i }()
|
queryType *string = func() *string { i := "hashtags"; return &i }()
|
||||||
following *bool = nil
|
following *bool = nil
|
||||||
|
fromAccountID *string = nil
|
||||||
expectedHTTPStatus = http.StatusOK
|
expectedHTTPStatus = http.StatusOK
|
||||||
expectedBody = ``
|
expectedBody = ``
|
||||||
)
|
)
|
||||||
|
@ -1537,6 +1691,7 @@ func (suite *SearchGetTestSuite) TestSearchNotHashtagButWithTypeHashtag() {
|
||||||
queryType,
|
queryType,
|
||||||
resolve,
|
resolve,
|
||||||
following,
|
following,
|
||||||
|
fromAccountID,
|
||||||
expectedHTTPStatus,
|
expectedHTTPStatus,
|
||||||
expectedBody)
|
expectedBody)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1562,6 +1717,7 @@ func (suite *SearchGetTestSuite) TestSearchBlockedAccountFullNamestring() {
|
||||||
query = "@" + targetAccount.Username + "@" + targetAccount.Domain
|
query = "@" + targetAccount.Username + "@" + targetAccount.Domain
|
||||||
queryType *string = func() *string { i := "accounts"; return &i }()
|
queryType *string = func() *string { i := "accounts"; return &i }()
|
||||||
following *bool = nil
|
following *bool = nil
|
||||||
|
fromAccountID *string = nil
|
||||||
expectedHTTPStatus = http.StatusOK
|
expectedHTTPStatus = http.StatusOK
|
||||||
expectedBody = ""
|
expectedBody = ""
|
||||||
)
|
)
|
||||||
|
@ -1593,6 +1749,7 @@ func (suite *SearchGetTestSuite) TestSearchBlockedAccountFullNamestring() {
|
||||||
queryType,
|
queryType,
|
||||||
resolve,
|
resolve,
|
||||||
following,
|
following,
|
||||||
|
fromAccountID,
|
||||||
expectedHTTPStatus,
|
expectedHTTPStatus,
|
||||||
expectedBody)
|
expectedBody)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1624,6 +1781,7 @@ func (suite *SearchGetTestSuite) TestSearchBlockedAccountPartialNamestring() {
|
||||||
query = "@" + targetAccount.Username
|
query = "@" + targetAccount.Username
|
||||||
queryType *string = func() *string { i := "accounts"; return &i }()
|
queryType *string = func() *string { i := "accounts"; return &i }()
|
||||||
following *bool = nil
|
following *bool = nil
|
||||||
|
fromAccountID *string = nil
|
||||||
expectedHTTPStatus = http.StatusOK
|
expectedHTTPStatus = http.StatusOK
|
||||||
expectedBody = ""
|
expectedBody = ""
|
||||||
)
|
)
|
||||||
|
@ -1655,6 +1813,7 @@ func (suite *SearchGetTestSuite) TestSearchBlockedAccountPartialNamestring() {
|
||||||
queryType,
|
queryType,
|
||||||
resolve,
|
resolve,
|
||||||
following,
|
following,
|
||||||
|
fromAccountID,
|
||||||
expectedHTTPStatus,
|
expectedHTTPStatus,
|
||||||
expectedBody)
|
expectedBody)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1683,6 +1842,7 @@ func (suite *SearchGetTestSuite) TestSearchBlockedAccountURI() {
|
||||||
query = targetAccount.URI
|
query = targetAccount.URI
|
||||||
queryType *string = func() *string { i := "accounts"; return &i }()
|
queryType *string = func() *string { i := "accounts"; return &i }()
|
||||||
following *bool = nil
|
following *bool = nil
|
||||||
|
fromAccountID *string = nil
|
||||||
expectedHTTPStatus = http.StatusOK
|
expectedHTTPStatus = http.StatusOK
|
||||||
expectedBody = ""
|
expectedBody = ""
|
||||||
)
|
)
|
||||||
|
@ -1714,6 +1874,7 @@ func (suite *SearchGetTestSuite) TestSearchBlockedAccountURI() {
|
||||||
queryType,
|
queryType,
|
||||||
resolve,
|
resolve,
|
||||||
following,
|
following,
|
||||||
|
fromAccountID,
|
||||||
expectedHTTPStatus,
|
expectedHTTPStatus,
|
||||||
expectedBody)
|
expectedBody)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -28,6 +28,7 @@ type SearchRequest struct {
|
||||||
Resolve bool
|
Resolve bool
|
||||||
Following bool
|
Following bool
|
||||||
ExcludeUnreviewed bool
|
ExcludeUnreviewed bool
|
||||||
|
AccountID string
|
||||||
APIv1 bool // Set to 'true' if using version 1 of the search API.
|
APIv1 bool // Set to 'true' if using version 1 of the search API.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -55,6 +55,7 @@
|
||||||
SearchQueryKey = "q"
|
SearchQueryKey = "q"
|
||||||
SearchResolveKey = "resolve"
|
SearchResolveKey = "resolve"
|
||||||
SearchTypeKey = "type"
|
SearchTypeKey = "type"
|
||||||
|
SearchAccountIDKey = "account_id"
|
||||||
|
|
||||||
/* Tag keys */
|
/* Tag keys */
|
||||||
|
|
||||||
|
|
|
@ -266,8 +266,9 @@ func (s *searchDB) accountText(following bool) *bun.SelectQuery {
|
||||||
// ORDER BY "status"."id" DESC LIMIT 10
|
// ORDER BY "status"."id" DESC LIMIT 10
|
||||||
func (s *searchDB) SearchForStatuses(
|
func (s *searchDB) SearchForStatuses(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
accountID string,
|
requestingAccountID string,
|
||||||
query string,
|
query string,
|
||||||
|
fromAccountID string,
|
||||||
maxID string,
|
maxID string,
|
||||||
minID string,
|
minID string,
|
||||||
limit int,
|
limit int,
|
||||||
|
@ -295,9 +296,12 @@ func (s *searchDB) SearchForStatuses(
|
||||||
// accountID or replying to accountID.
|
// accountID or replying to accountID.
|
||||||
WhereGroup(" AND ", func(q *bun.SelectQuery) *bun.SelectQuery {
|
WhereGroup(" AND ", func(q *bun.SelectQuery) *bun.SelectQuery {
|
||||||
return q.
|
return q.
|
||||||
Where("? = ?", bun.Ident("status.account_id"), accountID).
|
Where("? = ?", bun.Ident("status.account_id"), requestingAccountID).
|
||||||
WhereOr("? = ?", bun.Ident("status.in_reply_to_account_id"), accountID)
|
WhereOr("? = ?", bun.Ident("status.in_reply_to_account_id"), requestingAccountID)
|
||||||
})
|
})
|
||||||
|
if fromAccountID != "" {
|
||||||
|
q = q.Where("? = ?", bun.Ident("status.account_id"), fromAccountID)
|
||||||
|
}
|
||||||
|
|
||||||
// Return only items with a LOWER id than maxID.
|
// Return only items with a LOWER id than maxID.
|
||||||
if maxID == "" {
|
if maxID == "" {
|
||||||
|
|
|
@ -107,11 +107,22 @@ func (suite *SearchTestSuite) TestSearchAccountsFossAny() {
|
||||||
func (suite *SearchTestSuite) TestSearchStatuses() {
|
func (suite *SearchTestSuite) TestSearchStatuses() {
|
||||||
testAccount := suite.testAccounts["local_account_1"]
|
testAccount := suite.testAccounts["local_account_1"]
|
||||||
|
|
||||||
statuses, err := suite.db.SearchForStatuses(context.Background(), testAccount.ID, "hello", "", "", 10, 0)
|
statuses, err := suite.db.SearchForStatuses(context.Background(), testAccount.ID, "hello", "", "", "", 10, 0)
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
suite.Len(statuses, 1)
|
suite.Len(statuses, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (suite *SearchTestSuite) TestSearchStatusesFromAccount() {
|
||||||
|
testAccount := suite.testAccounts["local_account_1"]
|
||||||
|
fromAccount := suite.testAccounts["local_account_2"]
|
||||||
|
|
||||||
|
statuses, err := suite.db.SearchForStatuses(context.Background(), testAccount.ID, "hi", fromAccount.ID, "", "", 10, 0)
|
||||||
|
suite.NoError(err)
|
||||||
|
if suite.Len(statuses, 1) {
|
||||||
|
suite.Equal(fromAccount.ID, statuses[0].AccountID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (suite *SearchTestSuite) TestSearchTags() {
|
func (suite *SearchTestSuite) TestSearchTags() {
|
||||||
// Search with full tag string.
|
// Search with full tag string.
|
||||||
tags, err := suite.db.SearchForTags(context.Background(), "welcome", "", "", 10, 0)
|
tags, err := suite.db.SearchForTags(context.Background(), "welcome", "", "", 10, 0)
|
||||||
|
|
|
@ -27,8 +27,9 @@ type Search interface {
|
||||||
// SearchForAccounts uses the given query text to search for accounts that accountID follows.
|
// SearchForAccounts uses the given query text to search for accounts that accountID follows.
|
||||||
SearchForAccounts(ctx context.Context, accountID string, query string, maxID string, minID string, limit int, following bool, offset int) ([]*gtsmodel.Account, error)
|
SearchForAccounts(ctx context.Context, accountID string, query string, maxID string, minID string, limit int, following bool, offset int) ([]*gtsmodel.Account, error)
|
||||||
|
|
||||||
// SearchForStatuses uses the given query text to search for statuses created by accountID, or in reply to accountID.
|
// SearchForStatuses uses the given query text to search for statuses created by requestingAccountID, or in reply to requestingAccountID.
|
||||||
SearchForStatuses(ctx context.Context, accountID string, query string, maxID string, minID string, limit int, offset int) ([]*gtsmodel.Status, error)
|
// If fromAccountID is used, the results are restricted to statuses created by fromAccountID.
|
||||||
|
SearchForStatuses(ctx context.Context, requestingAccountID string, query string, fromAccountID string, maxID string, minID string, limit int, offset int) ([]*gtsmodel.Status, error)
|
||||||
|
|
||||||
// SearchForTags searches for tags that start with the given query text (case insensitive).
|
// SearchForTags searches for tags that start with the given query text (case insensitive).
|
||||||
SearchForTags(ctx context.Context, query string, maxID string, minID string, limit int, offset int) ([]*gtsmodel.Tag, error)
|
SearchForTags(ctx context.Context, query string, maxID string, minID string, limit int, offset int) ([]*gtsmodel.Tag, error)
|
||||||
|
|
|
@ -62,14 +62,15 @@ func (p *Processor) Get(
|
||||||
req *apimodel.SearchRequest,
|
req *apimodel.SearchRequest,
|
||||||
) (*apimodel.SearchResult, gtserror.WithCode) {
|
) (*apimodel.SearchResult, gtserror.WithCode) {
|
||||||
var (
|
var (
|
||||||
maxID = req.MaxID
|
maxID = req.MaxID
|
||||||
minID = req.MinID
|
minID = req.MinID
|
||||||
limit = req.Limit
|
limit = req.Limit
|
||||||
offset = req.Offset
|
offset = req.Offset
|
||||||
query = strings.TrimSpace(req.Query) // Trim trailing/leading whitespace.
|
query = strings.TrimSpace(req.Query) // Trim trailing/leading whitespace.
|
||||||
queryType = strings.TrimSpace(strings.ToLower(req.QueryType)) // Trim trailing/leading whitespace; convert to lowercase.
|
queryType = strings.TrimSpace(strings.ToLower(req.QueryType)) // Trim trailing/leading whitespace; convert to lowercase.
|
||||||
resolve = req.Resolve
|
resolve = req.Resolve
|
||||||
following = req.Following
|
following = req.Following
|
||||||
|
fromAccountID = req.AccountID
|
||||||
|
|
||||||
// Include instance accounts in the first
|
// Include instance accounts in the first
|
||||||
// parts of this search. This will be
|
// parts of this search. This will be
|
||||||
|
@ -114,6 +115,7 @@ func (p *Processor) Get(
|
||||||
{"queryType", queryType},
|
{"queryType", queryType},
|
||||||
{"resolve", resolve},
|
{"resolve", resolve},
|
||||||
{"following", following},
|
{"following", following},
|
||||||
|
{"fromAccountID", fromAccountID},
|
||||||
}...).
|
}...).
|
||||||
Debugf("beginning search")
|
Debugf("beginning search")
|
||||||
|
|
||||||
|
@ -309,6 +311,7 @@ func (p *Processor) Get(
|
||||||
query,
|
query,
|
||||||
queryType,
|
queryType,
|
||||||
following,
|
following,
|
||||||
|
fromAccountID,
|
||||||
appendAccount,
|
appendAccount,
|
||||||
appendStatus,
|
appendStatus,
|
||||||
); err != nil && !errors.Is(err, db.ErrNoEntries) {
|
); err != nil && !errors.Is(err, db.ErrNoEntries) {
|
||||||
|
@ -743,6 +746,7 @@ func (p *Processor) byText(
|
||||||
query string,
|
query string,
|
||||||
queryType string,
|
queryType string,
|
||||||
following bool,
|
following bool,
|
||||||
|
fromAccountID string,
|
||||||
appendAccount func(*gtsmodel.Account),
|
appendAccount func(*gtsmodel.Account),
|
||||||
appendStatus func(*gtsmodel.Status),
|
appendStatus func(*gtsmodel.Status),
|
||||||
) error {
|
) error {
|
||||||
|
@ -779,6 +783,7 @@ func (p *Processor) byText(
|
||||||
limit,
|
limit,
|
||||||
offset,
|
offset,
|
||||||
query,
|
query,
|
||||||
|
fromAccountID,
|
||||||
appendStatus,
|
appendStatus,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -826,12 +831,30 @@ func (p *Processor) statusesByText(
|
||||||
limit int,
|
limit int,
|
||||||
offset int,
|
offset int,
|
||||||
query string,
|
query string,
|
||||||
|
fromAccountID string,
|
||||||
appendStatus func(*gtsmodel.Status),
|
appendStatus func(*gtsmodel.Status),
|
||||||
) error {
|
) error {
|
||||||
|
parsed, err := p.parseQuery(ctx, query)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
query = parsed.query
|
||||||
|
// If the owning account for statuses was not provided as the account_id query parameter,
|
||||||
|
// it may still have been provided as a search operator in the query string.
|
||||||
|
if fromAccountID == "" {
|
||||||
|
fromAccountID = parsed.fromAccountID
|
||||||
|
}
|
||||||
|
|
||||||
statuses, err := p.state.DB.SearchForStatuses(
|
statuses, err := p.state.DB.SearchForStatuses(
|
||||||
ctx,
|
ctx,
|
||||||
requestingAccountID,
|
requestingAccountID,
|
||||||
query, maxID, minID, limit, offset)
|
query,
|
||||||
|
fromAccountID,
|
||||||
|
maxID,
|
||||||
|
minID,
|
||||||
|
limit,
|
||||||
|
offset,
|
||||||
|
)
|
||||||
if err != nil && !errors.Is(err, db.ErrNoEntries) {
|
if err != nil && !errors.Is(err, db.ErrNoEntries) {
|
||||||
return gtserror.Newf("error checking database for statuses using text %s: %w", query, err)
|
return gtserror.Newf("error checking database for statuses using text %s: %w", query, err)
|
||||||
}
|
}
|
||||||
|
@ -842,3 +865,60 @@ func (p *Processor) statusesByText(
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// parsedQuery represents the results of parsing the search operator terms within a query.
|
||||||
|
type parsedQuery struct {
|
||||||
|
// query is the original search query text with operator terms removed.
|
||||||
|
query string
|
||||||
|
// fromAccountID is the account from a successfully resolved `from:` operator, if present.
|
||||||
|
fromAccountID string
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseQuery parses query text and handles any search operator terms present.
|
||||||
|
func (p *Processor) parseQuery(ctx context.Context, query string) (parsed parsedQuery, err error) {
|
||||||
|
queryPartSeparator := " "
|
||||||
|
queryParts := strings.Split(query, queryPartSeparator)
|
||||||
|
nonOperatorQueryParts := make([]string, 0, len(queryParts))
|
||||||
|
for _, queryPart := range queryParts {
|
||||||
|
if arg, hasPrefix := strings.CutPrefix(queryPart, "from:"); hasPrefix {
|
||||||
|
parsed.fromAccountID, err = p.parseFromOperatorArg(ctx, arg)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
nonOperatorQueryParts = append(nonOperatorQueryParts, queryPart)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
parsed.query = strings.Join(nonOperatorQueryParts, queryPartSeparator)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseFromOperatorArg attempts to parse the from: operator's argument as an account name,
|
||||||
|
// and returns the account ID if possible. Allows specifying an account name with or without a leading @.
|
||||||
|
func (p *Processor) parseFromOperatorArg(ctx context.Context, namestring string) (string, error) {
|
||||||
|
if namestring == "" {
|
||||||
|
return "", gtserror.New(
|
||||||
|
"the 'from:' search operator requires an account name, but it wasn't provided",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if namestring[0] != '@' {
|
||||||
|
namestring = "@" + namestring
|
||||||
|
}
|
||||||
|
|
||||||
|
username, domain, err := util.ExtractNamestringParts(namestring)
|
||||||
|
if err != nil {
|
||||||
|
return "", gtserror.Newf(
|
||||||
|
"the 'from:' search operator couldn't parse its argument as an account name: %w",
|
||||||
|
err,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
account, err := p.state.DB.GetAccountByUsernameDomain(gtscontext.SetBarebones(ctx), username, domain)
|
||||||
|
if err != nil {
|
||||||
|
return "", gtserror.Newf(
|
||||||
|
"the 'from:' search operator couldn't find the requested account name: %w",
|
||||||
|
err,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return account.ID, nil
|
||||||
|
}
|
||||||
|
|
|
@ -62,6 +62,7 @@ nav:
|
||||||
- "User Guide":
|
- "User Guide":
|
||||||
- "user_guide/posts.md"
|
- "user_guide/posts.md"
|
||||||
- "user_guide/settings.md"
|
- "user_guide/settings.md"
|
||||||
|
- "user_guide/search.md"
|
||||||
- "user_guide/custom_css.md"
|
- "user_guide/custom_css.md"
|
||||||
- "user_guide/password_management.md"
|
- "user_guide/password_management.md"
|
||||||
- "user_guide/rss.md"
|
- "user_guide/rss.md"
|
||||||
|
|
Loading…
Reference in a new issue