mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2026-01-08 07:13:17 -06:00
[feature] Implement markers API (#1989)
* Implement markers API Fixes #1856 * Correct import grouping in markers files * Regenerate Swagger for markers API * Shorten names for readability * Cache markers for 6 hours * Update DB ref * Update envparsing.sh
This commit is contained in:
parent
cf4bd700fb
commit
b874e9251e
29 changed files with 1083 additions and 3 deletions
45
internal/api/client/markers/markers.go
Normal file
45
internal/api/client/markers/markers.go
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
// 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 markers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/processing"
|
||||
)
|
||||
|
||||
const (
|
||||
// BasePath is the base path for serving the markers API, minus the 'api' prefix
|
||||
BasePath = "/v1/markers"
|
||||
)
|
||||
|
||||
type Module struct {
|
||||
processor *processing.Processor
|
||||
}
|
||||
|
||||
func New(processor *processing.Processor) *Module {
|
||||
return &Module{
|
||||
processor: processor,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Module) Route(attachHandler func(method string, path string, f ...gin.HandlerFunc) gin.IRoutes) {
|
||||
attachHandler(http.MethodGet, BasePath, m.MarkersGETHandler)
|
||||
attachHandler(http.MethodPost, BasePath, m.MarkersPOSTHandler)
|
||||
}
|
||||
108
internal/api/client/markers/markersget.go
Normal file
108
internal/api/client/markers/markersget.go
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
// 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 markers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/validate"
|
||||
)
|
||||
|
||||
// MarkersGETHandler swagger:operation GET /api/v1/markers markersGet
|
||||
//
|
||||
// Get timeline markers by name
|
||||
//
|
||||
// ---
|
||||
// tags:
|
||||
// - markers
|
||||
//
|
||||
// produces:
|
||||
// - application/json
|
||||
//
|
||||
// parameters:
|
||||
// -
|
||||
// name: timeline
|
||||
// type: array
|
||||
// items:
|
||||
// type: string
|
||||
// enum:
|
||||
// - home
|
||||
// - notifications
|
||||
// description: Timelines to retrieve.
|
||||
// in: query
|
||||
//
|
||||
// security:
|
||||
// - OAuth2 Bearer:
|
||||
// - read:statuses
|
||||
//
|
||||
// responses:
|
||||
// '200':
|
||||
// description: Requested markers
|
||||
// schema:
|
||||
// "$ref": "#/definitions/markers"
|
||||
// '400':
|
||||
// description: bad request
|
||||
// '401':
|
||||
// description: unauthorized
|
||||
// '500':
|
||||
// description: internal server error
|
||||
func (m *Module) MarkersGETHandler(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
|
||||
}
|
||||
|
||||
names, errWithCode := parseMarkerNames(c.QueryArray("timeline[]"))
|
||||
if errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
}
|
||||
|
||||
marker, errWithCode := m.processor.Markers().Get(c.Request.Context(), authed.Account, names)
|
||||
if errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, marker)
|
||||
}
|
||||
|
||||
// parseMarkerNames turns a list of strings into a set of valid marker timeline names, or returns an error.
|
||||
func parseMarkerNames(nameStrings []string) ([]apimodel.MarkerName, gtserror.WithCode) {
|
||||
nameSet := make(map[apimodel.MarkerName]struct{}, apimodel.MarkerNameNumValues)
|
||||
for _, timelineString := range nameStrings {
|
||||
if err := validate.MarkerName(timelineString); err != nil {
|
||||
return nil, gtserror.NewErrorBadRequest(err, err.Error())
|
||||
}
|
||||
nameSet[apimodel.MarkerName(timelineString)] = struct{}{}
|
||||
}
|
||||
|
||||
i := 0
|
||||
names := make([]apimodel.MarkerName, len(nameSet))
|
||||
for name := range nameSet {
|
||||
names[i] = name
|
||||
i++
|
||||
}
|
||||
|
||||
return names, nil
|
||||
}
|
||||
110
internal/api/client/markers/markerspost.go
Normal file
110
internal/api/client/markers/markerspost.go
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
// 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 markers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
||||
// MarkersPOSTHandler swagger:operation POST /api/v1/markers markersPost
|
||||
//
|
||||
// Update timeline markers by name
|
||||
//
|
||||
// ---
|
||||
// tags:
|
||||
// - markers
|
||||
//
|
||||
// consumes:
|
||||
// - multipart/form-data
|
||||
//
|
||||
// produces:
|
||||
// - application/json
|
||||
//
|
||||
// parameters:
|
||||
// -
|
||||
// name: home[last_read_id]
|
||||
// type: string
|
||||
// description: Last status ID read on the home timeline.
|
||||
// in: formData
|
||||
// -
|
||||
// name: notifications[last_read_id]
|
||||
// type: string
|
||||
// description: Last notification ID read on the notifications timeline.
|
||||
// in: formData
|
||||
//
|
||||
// security:
|
||||
// - OAuth2 Bearer:
|
||||
// - write:statuses
|
||||
//
|
||||
// responses:
|
||||
// '200':
|
||||
// description: Requested markers
|
||||
// schema:
|
||||
// "$ref": "#/definitions/markers"
|
||||
// '400':
|
||||
// description: bad request
|
||||
// '401':
|
||||
// description: unauthorized
|
||||
// '409':
|
||||
// description: conflict (when two clients try to update the same timeline at the same time)
|
||||
// '500':
|
||||
// description: internal server error
|
||||
func (m *Module) MarkersPOSTHandler(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
|
||||
}
|
||||
|
||||
form := &apimodel.MarkerPostRequest{}
|
||||
if err := c.ShouldBind(form); err != nil {
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
markers := make([]*gtsmodel.Marker, 0, apimodel.MarkerNameNumValues)
|
||||
if homeLastReadID := form.HomeLastReadID(); homeLastReadID != "" {
|
||||
markers = append(markers, >smodel.Marker{
|
||||
AccountID: authed.Account.ID,
|
||||
Name: gtsmodel.MarkerNameHome,
|
||||
LastReadID: homeLastReadID,
|
||||
})
|
||||
}
|
||||
if notificationsLastReadID := form.NotificationsLastReadID(); notificationsLastReadID != "" {
|
||||
markers = append(markers, >smodel.Marker{
|
||||
AccountID: authed.Account.ID,
|
||||
Name: gtsmodel.MarkerNameNotifications,
|
||||
LastReadID: notificationsLastReadID,
|
||||
})
|
||||
}
|
||||
|
||||
marker, errWithCode := m.processor.Markers().Update(c.Request.Context(), markers)
|
||||
if errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, marker)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue