summaryrefslogtreecommitdiff
path: root/internal/api/client/timelines
diff options
context:
space:
mode:
authorLibravatar tobi <31960611+tsmethurst@users.noreply.github.com>2023-07-31 15:47:35 +0200
committerLibravatar GitHub <noreply@github.com>2023-07-31 15:47:35 +0200
commit2796a2e82f16ade9872008878cf88299bd66b4e7 (patch)
tree76f7b69cc1da57ca10b71c57abf1892575bea100 /internal/api/client/timelines
parent[performance] cache follow, follow request and block ID lists (#2027) (diff)
downloadgotosocial-2796a2e82f16ade9872008878cf88299bd66b4e7.tar.xz
[feature] Hashtag federation (in/out), hashtag client API endpoints (#2032)
* update go-fed * do the things * remove unused columns from tags * update to latest lingo from main * further tag shenanigans * serve stub page at tag endpoint * we did it lads * tests, oh tests, ohhh tests, oh tests (doo doo doo doo) * swagger docs * document hashtag usage + federation * instanceGet * don't bother parsing tag href * rename whereStartsWith -> whereStartsLike * remove GetOrCreateTag * dont cache status tag timelineability
Diffstat (limited to 'internal/api/client/timelines')
-rw-r--r--internal/api/client/timelines/home.go6
-rw-r--r--internal/api/client/timelines/list.go15
-rw-r--r--internal/api/client/timelines/public.go6
-rw-r--r--internal/api/client/timelines/tag.go146
-rw-r--r--internal/api/client/timelines/timeline.go23
5 files changed, 164 insertions, 32 deletions
diff --git a/internal/api/client/timelines/home.go b/internal/api/client/timelines/home.go
index c3f075d5e..963096f59 100644
--- a/internal/api/client/timelines/home.go
+++ b/internal/api/client/timelines/home.go
@@ -133,9 +133,9 @@ func (m *Module) HomeTimelineGETHandler(c *gin.Context) {
resp, errWithCode := m.processor.Timeline().HomeTimelineGet(
c.Request.Context(),
authed,
- c.Query(MaxIDKey),
- c.Query(SinceIDKey),
- c.Query(MinIDKey),
+ c.Query(apiutil.MaxIDKey),
+ c.Query(apiutil.SinceIDKey),
+ c.Query(apiutil.MinIDKey),
limit,
local,
)
diff --git a/internal/api/client/timelines/list.go b/internal/api/client/timelines/list.go
index 8b4f7fad9..2e13e32cd 100644
--- a/internal/api/client/timelines/list.go
+++ b/internal/api/client/timelines/list.go
@@ -18,7 +18,6 @@
package timelines
import (
- "errors"
"net/http"
"github.com/gin-gonic/gin"
@@ -118,11 +117,9 @@ func (m *Module) ListTimelineGETHandler(c *gin.Context) {
return
}
- targetListID := c.Param(IDKey)
- if targetListID == "" {
- err := errors.New("no list id specified")
- apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGetV1)
- return
+ targetListID, errWithCode := apiutil.ParseID(c.Param(apiutil.IDKey))
+ if errWithCode != nil {
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
}
limit, errWithCode := apiutil.ParseLimit(c.Query(apiutil.LimitKey), 20, 40, 1)
@@ -135,9 +132,9 @@ func (m *Module) ListTimelineGETHandler(c *gin.Context) {
c.Request.Context(),
authed,
targetListID,
- c.Query(MaxIDKey),
- c.Query(SinceIDKey),
- c.Query(MinIDKey),
+ c.Query(apiutil.MaxIDKey),
+ c.Query(apiutil.SinceIDKey),
+ c.Query(apiutil.MinIDKey),
limit,
)
if errWithCode != nil {
diff --git a/internal/api/client/timelines/public.go b/internal/api/client/timelines/public.go
index 96958e6a4..7b8acf1ca 100644
--- a/internal/api/client/timelines/public.go
+++ b/internal/api/client/timelines/public.go
@@ -144,9 +144,9 @@ func (m *Module) PublicTimelineGETHandler(c *gin.Context) {
resp, errWithCode := m.processor.Timeline().PublicTimelineGet(
c.Request.Context(),
authed,
- c.Query(MaxIDKey),
- c.Query(SinceIDKey),
- c.Query(MinIDKey),
+ c.Query(apiutil.MaxIDKey),
+ c.Query(apiutil.SinceIDKey),
+ c.Query(apiutil.MinIDKey),
limit,
local,
)
diff --git a/internal/api/client/timelines/tag.go b/internal/api/client/timelines/tag.go
new file mode 100644
index 000000000..58754705b
--- /dev/null
+++ b/internal/api/client/timelines/tag.go
@@ -0,0 +1,146 @@
+// GoToSocial
+// Copyright (C) GoToSocial Authors admin@gotosocial.org
+// SPDX-License-Identifier: AGPL-3.0-or-later
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+package timelines
+
+import (
+ "net/http"
+
+ "github.com/gin-gonic/gin"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
+ "github.com/superseriousbusiness/gotosocial/internal/gtserror"
+ "github.com/superseriousbusiness/gotosocial/internal/oauth"
+)
+
+// HomeTimelineGETHandler swagger:operation GET /api/v1/timelines/tag/{tag_name} tagTimeline
+//
+// See public statuses that use the given hashtag (case insensitive).
+//
+// The statuses will be returned in descending chronological order (newest first), with sequential IDs (bigger = newer).
+//
+// The returned Link header can be used to generate the previous and next queries when scrolling up or down a timeline.
+//
+// Example:
+//
+// ```
+// <https://example.org/api/v1/timelines/tag/example?limit=20&max_id=01FC3GSQ8A3MMJ43BPZSGEG29M>; rel="next", <https://example.org/api/v1/timelines/tag/example?limit=20&min_id=01FC3KJW2GYXSDDRA6RWNDM46M>; rel="prev"
+// ````
+//
+// ---
+// tags:
+// - timelines
+//
+// produces:
+// - application/json
+//
+// parameters:
+// -
+// name: max_id
+// type: string
+// description: >-
+// Return only statuses *OLDER* than the given max status ID.
+// The status with the specified ID will not be included in the response.
+// in: query
+// required: false
+// -
+// name: since_id
+// type: string
+// description: >-
+// Return only statuses *newer* than the given since status ID.
+// The status with the specified ID will not be included in the response.
+// in: query
+// -
+// name: min_id
+// type: string
+// description: >-
+// Return only statuses *immediately newer* than the given since status ID.
+// The status with the specified ID will not be included in the response.
+// in: query
+// required: false
+// -
+// name: limit
+// type: integer
+// description: Number of statuses to return.
+// default: 20
+// minimum: 1
+// maximum: 40
+// in: query
+// required: false
+//
+// security:
+// - OAuth2 Bearer:
+// - read:statuses
+//
+// responses:
+// '200':
+// name: statuses
+// description: Array of statuses.
+// schema:
+// type: array
+// items:
+// "$ref": "#/definitions/status"
+// headers:
+// Link:
+// type: string
+// description: Links to the next and previous queries.
+// '401':
+// description: unauthorized
+// '400':
+// description: bad request
+func (m *Module) TagTimelineGETHandler(c *gin.Context) {
+ authed, err := oauth.Authed(c, true, true, true, true)
+ if err != nil {
+ apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGetV1)
+ return
+ }
+
+ if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGetV1)
+ return
+ }
+
+ limit, errWithCode := apiutil.ParseLimit(c.Query(apiutil.LimitKey), 20, 40, 1)
+ if errWithCode != nil {
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
+ return
+ }
+
+ tagName, errWithCode := apiutil.ParseTagName(c.Param(apiutil.TagNameKey))
+ if errWithCode != nil {
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
+ return
+ }
+
+ resp, errWithCode := m.processor.Timeline().TagTimelineGet(
+ c.Request.Context(),
+ authed.Account,
+ tagName,
+ c.Query(apiutil.MaxIDKey),
+ c.Query(apiutil.SinceIDKey),
+ c.Query(apiutil.MinIDKey),
+ limit,
+ )
+ if errWithCode != nil {
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
+ return
+ }
+
+ if resp.LinkHeader != "" {
+ c.Header("Link", resp.LinkHeader)
+ }
+ c.JSON(http.StatusOK, resp.Items)
+}
diff --git a/internal/api/client/timelines/timeline.go b/internal/api/client/timelines/timeline.go
index 2580333d9..2362ca47e 100644
--- a/internal/api/client/timelines/timeline.go
+++ b/internal/api/client/timelines/timeline.go
@@ -21,28 +21,16 @@ import (
"net/http"
"github.com/gin-gonic/gin"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/processing"
)
const (
- // BasePath is the base URI path for serving timelines, minus the 'api' prefix.
- BasePath = "/v1/timelines"
- IDKey = "id"
- // HomeTimeline is the path for the home timeline
- HomeTimeline = BasePath + "/home"
- // PublicTimeline is the path for the public (and public local) timeline
+ BasePath = "/v1/timelines"
+ HomeTimeline = BasePath + "/home"
PublicTimeline = BasePath + "/public"
- ListTimeline = BasePath + "/list/:" + IDKey
- // MaxIDKey is the url query for setting a max status ID to return
- MaxIDKey = "max_id"
- // SinceIDKey is the url query for returning results newer than the given ID
- SinceIDKey = "since_id"
- // MinIDKey is the url query for returning results immediately newer than the given ID
- MinIDKey = "min_id"
- // LimitKey is for specifying maximum number of results to return.
- LimitKey = "limit"
- // LocalKey is for specifying whether only local statuses should be returned
- LocalKey = "local"
+ ListTimeline = BasePath + "/list/:" + apiutil.IDKey
+ TagTimeline = BasePath + "/tag/:" + apiutil.TagNameKey
)
type Module struct {
@@ -59,4 +47,5 @@ func (m *Module) Route(attachHandler func(method string, path string, f ...gin.H
attachHandler(http.MethodGet, HomeTimeline, m.HomeTimelineGETHandler)
attachHandler(http.MethodGet, PublicTimeline, m.PublicTimelineGETHandler)
attachHandler(http.MethodGet, ListTimeline, m.ListTimelineGETHandler)
+ attachHandler(http.MethodGet, TagTimeline, m.TagTimelineGETHandler)
}