diff options
Diffstat (limited to 'internal/api/client/lists')
-rw-r--r-- | internal/api/client/lists/list.go | 19 | ||||
-rw-r--r-- | internal/api/client/lists/listaccounts.go | 156 | ||||
-rw-r--r-- | internal/api/client/lists/listaccountsadd.go | 120 | ||||
-rw-r--r-- | internal/api/client/lists/listaccountsremove.go | 120 | ||||
-rw-r--r-- | internal/api/client/lists/listcreate.go | 106 | ||||
-rw-r--r-- | internal/api/client/lists/listdelete.go | 91 | ||||
-rw-r--r-- | internal/api/client/lists/listget.go | 95 | ||||
-rw-r--r-- | internal/api/client/lists/listsget.go (renamed from internal/api/client/lists/listsgets.go) | 46 | ||||
-rw-r--r-- | internal/api/client/lists/listupdate.go | 152 |
9 files changed, 900 insertions, 5 deletions
diff --git a/internal/api/client/lists/list.go b/internal/api/client/lists/list.go index b1c193397..515075271 100644 --- a/internal/api/client/lists/list.go +++ b/internal/api/client/lists/list.go @@ -25,8 +25,15 @@ import ( ) const ( + IDKey = "id" // BasePath is the base path for serving the lists API, minus the 'api' prefix - BasePath = "/v1/lists" + BasePath = "/v1/lists" + BasePathWithID = BasePath + "/:" + IDKey + AccountsPath = BasePathWithID + "/accounts" + MaxIDKey = "max_id" + LimitKey = "limit" + SinceIDKey = "since_id" + MinIDKey = "min_id" ) type Module struct { @@ -40,5 +47,15 @@ func New(processor *processing.Processor) *Module { } func (m *Module) Route(attachHandler func(method string, path string, f ...gin.HandlerFunc) gin.IRoutes) { + // create / get / update / delete lists + attachHandler(http.MethodPost, BasePath, m.ListCreatePOSTHandler) attachHandler(http.MethodGet, BasePath, m.ListsGETHandler) + attachHandler(http.MethodGet, BasePathWithID, m.ListGETHandler) + attachHandler(http.MethodPut, BasePathWithID, m.ListUpdatePUTHandler) + attachHandler(http.MethodDelete, BasePathWithID, m.ListDELETEHandler) + + // get / add / remove list accounts + attachHandler(http.MethodGet, AccountsPath, m.ListAccountsGETHandler) + attachHandler(http.MethodPost, AccountsPath, m.ListAccountsPOSTHandler) + attachHandler(http.MethodDelete, AccountsPath, m.ListAccountsDELETEHandler) } diff --git a/internal/api/client/lists/listaccounts.go b/internal/api/client/lists/listaccounts.go new file mode 100644 index 000000000..3a24cab27 --- /dev/null +++ b/internal/api/client/lists/listaccounts.go @@ -0,0 +1,156 @@ +// 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 lists + +import ( + "errors" + "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" +) + +// ListAccountsGETHandler swagger:operation GET /api/v1/list/{id}/accounts listAccounts +// +// Page through accounts in this list. +// +// 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/list/01H0W619198FX7J54NF7EH1NG2/accounts?limit=20&max_id=01FC3GSQ8A3MMJ43BPZSGEG29M>; rel="next", <https://example.org/api/v1/list/01H0W619198FX7J54NF7EH1NG2/accounts?limit=20&min_id=01FC3KJW2GYXSDDRA6RWNDM46M>; rel="prev" +// ```` +// +// --- +// tags: +// - lists +// +// produces: +// - application/json +// +// parameters: +// - +// name: id +// type: string +// description: ID of the list +// in: path +// required: true +// - +// name: max_id +// type: string +// description: >- +// Return only list entries *OLDER* than the given max ID. +// The account from the list entry with the specified ID will not be included in the response. +// in: query +// required: false +// - +// name: since_id +// type: string +// description: >- +// Return only list entries *NEWER* than the given since ID. +// The account from the list entry with the specified ID will not be included in the response. +// in: query +// - +// name: min_id +// type: string +// description: >- +// Return only list entries *IMMEDIATELY NEWER* than the given min ID. +// The account from the list entry with the specified ID will not be included in the response. +// in: query +// required: false +// - +// name: limit +// type: integer +// description: Number of accounts to return. +// default: 20 +// in: query +// required: false +// +// security: +// - OAuth2 Bearer: +// - read:lists +// +// responses: +// '200': +// headers: +// Link: +// type: string +// description: Links to the next and previous queries. +// name: accounts +// description: Array of accounts. +// schema: +// type: array +// items: +// "$ref": "#/definitions/account" +// '400': +// description: bad request +// '401': +// description: unauthorized +// '404': +// description: not found +// '406': +// description: not acceptable +// '500': +// description: internal server error +func (m *Module) ListAccountsGETHandler(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 + } + + 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 + } + + limit, errWithCode := apiutil.ParseLimit(c.Query(apiutil.LimitKey), 20) + if errWithCode != nil { + apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1) + return + } + + resp, errWithCode := m.processor.List().GetListAccounts( + c.Request.Context(), + authed.Account, + targetListID, + c.Query(MaxIDKey), + c.Query(SinceIDKey), + c.Query(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/lists/listaccountsadd.go b/internal/api/client/lists/listaccountsadd.go new file mode 100644 index 000000000..5cf907b06 --- /dev/null +++ b/internal/api/client/lists/listaccountsadd.go @@ -0,0 +1,120 @@ +// 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 lists + +import ( + "errors" + "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" +) + +// ListAccountsPOSTHandler swagger:operation POST /api/v1/list/{id}/accounts addListAccounts +// +// Add one or more accounts to the given list. +// +// --- +// tags: +// - lists +// +// consumes: +// - application/json +// - application/xml +// - application/x-www-form-urlencoded +// +// produces: +// - application/json +// +// parameters: +// - +// name: id +// type: string +// description: ID of the list +// in: path +// required: true +// - +// name: account_ids +// type: array +// items: +// type: string +// description: >- +// Array of accountIDs to modify. +// Each accountID must correspond to an account +// that the requesting account follows. +// in: formData +// required: true +// +// security: +// - OAuth2 Bearer: +// - read:lists +// +// responses: +// '200': +// description: list accounts updated +// '400': +// description: bad request +// '401': +// description: unauthorized +// '404': +// description: not found +// '406': +// description: not acceptable +// '500': +// description: internal server error +func (m *Module) ListAccountsPOSTHandler(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 + } + + 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 + } + + form := &apimodel.ListAccountsChangeRequest{} + if err := c.ShouldBind(form); err != nil { + apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGetV1) + return + } + + if len(form.AccountIDs) == 0 { + err := errors.New("no account IDs given") + apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGetV1) + return + } + + if errWithCode := m.processor.List().AddToList(c.Request.Context(), authed.Account, targetListID, form.AccountIDs); errWithCode != nil { + apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1) + return + } + + c.JSON(http.StatusOK, gin.H{}) +} diff --git a/internal/api/client/lists/listaccountsremove.go b/internal/api/client/lists/listaccountsremove.go new file mode 100644 index 000000000..6ce7e3cd3 --- /dev/null +++ b/internal/api/client/lists/listaccountsremove.go @@ -0,0 +1,120 @@ +// 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 lists + +import ( + "errors" + "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" +) + +// ListAccountsDELETEHandler swagger:operation DELETE /api/v1/list/{id}/accounts removeListAccounts +// +// Remove one or more accounts from the given list. +// +// --- +// tags: +// - lists +// +// consumes: +// - application/json +// - application/xml +// - application/x-www-form-urlencoded +// +// produces: +// - application/json +// +// parameters: +// - +// name: id +// type: string +// description: ID of the list +// in: path +// required: true +// - +// name: account_ids +// type: array +// items: +// type: string +// description: >- +// Array of accountIDs to modify. +// Each accountID must correspond to an account +// that the requesting account follows. +// in: formData +// required: true +// +// security: +// - OAuth2 Bearer: +// - read:lists +// +// responses: +// '200': +// description: list accounts updated +// '400': +// description: bad request +// '401': +// description: unauthorized +// '404': +// description: not found +// '406': +// description: not acceptable +// '500': +// description: internal server error +func (m *Module) ListAccountsDELETEHandler(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 + } + + 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 + } + + form := &apimodel.ListAccountsChangeRequest{} + if err := c.ShouldBind(form); err != nil { + apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGetV1) + return + } + + if len(form.AccountIDs) == 0 { + err := errors.New("no account IDs given") + apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGetV1) + return + } + + if errWithCode := m.processor.List().RemoveFromList(c.Request.Context(), authed.Account, targetListID, form.AccountIDs); errWithCode != nil { + apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1) + return + } + + c.JSON(http.StatusOK, gin.H{}) +} diff --git a/internal/api/client/lists/listcreate.go b/internal/api/client/lists/listcreate.go new file mode 100644 index 000000000..09a654c74 --- /dev/null +++ b/internal/api/client/lists/listcreate.go @@ -0,0 +1,106 @@ +// 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 lists + +import ( + "net/http" + "strings" + + "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" + "github.com/superseriousbusiness/gotosocial/internal/validate" +) + +// ListCreatePOSTHandler swagger:operation POST /api/v1/list listCreate +// +// Create a new list. +// +// --- +// tags: +// - lists +// +// consumes: +// - application/json +// - application/xml +// - application/x-www-form-urlencoded +// +// produces: +// - application/json +// +// security: +// - OAuth2 Bearer: +// - write:lists +// +// responses: +// '200': +// description: "The newly created list." +// schema: +// "$ref": "#/definitions/list" +// '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) ListCreatePOSTHandler(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 + } + + form := &apimodel.ListCreateRequest{} + if err := c.ShouldBind(form); err != nil { + apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGetV1) + return + } + + if err := validate.ListTitle(form.Title); err != nil { + apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGetV1) + return + } + + repliesPolicy := gtsmodel.RepliesPolicy(strings.ToLower(form.RepliesPolicy)) + if err := validate.ListRepliesPolicy(repliesPolicy); err != nil { + apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGetV1) + return + } + + apiList, errWithCode := m.processor.List().Create(c.Request.Context(), authed.Account, form.Title, repliesPolicy) + if errWithCode != nil { + apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1) + return + } + + c.JSON(http.StatusOK, apiList) +} diff --git a/internal/api/client/lists/listdelete.go b/internal/api/client/lists/listdelete.go new file mode 100644 index 000000000..394ddfb6b --- /dev/null +++ b/internal/api/client/lists/listdelete.go @@ -0,0 +1,91 @@ +// 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 lists + +import ( + "errors" + "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" +) + +// ListDELETEHandler swagger:operation DELETE /api/v1/list/{id} listDelete +// +// Delete a single list with the given ID. +// +// --- +// tags: +// - lists +// +// produces: +// - application/json +// +// parameters: +// - +// name: id +// type: string +// description: ID of the list +// in: path +// required: true +// +// security: +// - OAuth2 Bearer: +// - write:lists +// +// responses: +// '200': +// description: list deleted +// '400': +// description: bad request +// '401': +// description: unauthorized +// '404': +// description: not found +// '406': +// description: not acceptable +// '500': +// description: internal server error +func (m *Module) ListDELETEHandler(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 + } + + 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 + } + + if errWithCode := m.processor.List().Delete(c.Request.Context(), authed.Account, targetListID); errWithCode != nil { + apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1) + return + } + + c.JSON(http.StatusOK, gin.H{}) +} diff --git a/internal/api/client/lists/listget.go b/internal/api/client/lists/listget.go new file mode 100644 index 000000000..3aed594d4 --- /dev/null +++ b/internal/api/client/lists/listget.go @@ -0,0 +1,95 @@ +// 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 lists + +import ( + "errors" + "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" +) + +// ListGETHandler swagger:operation GET /api/v1/list/{id} list +// +// Get a single list with the given ID. +// +// --- +// tags: +// - lists +// +// produces: +// - application/json +// +// parameters: +// - +// name: id +// type: string +// description: ID of the list +// in: path +// required: true +// +// security: +// - OAuth2 Bearer: +// - read:lists +// +// responses: +// '200': +// name: list +// description: Requested list. +// schema: +// "$ref": "#/definitions/list" +// '400': +// description: bad request +// '401': +// description: unauthorized +// '404': +// description: not found +// '406': +// description: not acceptable +// '500': +// description: internal server error +func (m *Module) ListGETHandler(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 + } + + 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 + } + + resp, errWithCode := m.processor.List().Get(c.Request.Context(), authed.Account, targetListID) + if errWithCode != nil { + apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1) + return + } + + c.JSON(http.StatusOK, resp) +} diff --git a/internal/api/client/lists/listsgets.go b/internal/api/client/lists/listsget.go index 66b713611..f16152a9d 100644 --- a/internal/api/client/lists/listsgets.go +++ b/internal/api/client/lists/listsget.go @@ -26,9 +26,42 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/oauth" ) -// ListsGETHandler returns a list of lists created by/for the authed account +// ListsGETHandler swagger:operation GET /api/v1/lists lists +// +// Get all lists for owned by authorized user. +// +// --- +// tags: +// - lists +// +// produces: +// - application/json +// +// security: +// - OAuth2 Bearer: +// - read:lists +// +// responses: +// '200': +// name: lists +// description: Array of all lists owned by the requesting user. +// schema: +// type: array +// items: +// "$ref": "#/definitions/list" +// '400': +// description: bad request +// '401': +// description: unauthorized +// '404': +// description: not found +// '406': +// description: not acceptable +// '500': +// description: internal server error func (m *Module) ListsGETHandler(c *gin.Context) { - if _, err := oauth.Authed(c, true, true, true, true); err != nil { + authed, err := oauth.Authed(c, true, true, true, true) + if err != nil { apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGetV1) return } @@ -38,6 +71,11 @@ func (m *Module) ListsGETHandler(c *gin.Context) { return } - // todo: implement this; currently it's a no-op - c.JSON(http.StatusOK, []string{}) + lists, errWithCode := m.processor.List().GetAll(c.Request.Context(), authed.Account) + if errWithCode != nil { + apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1) + return + } + + c.JSON(http.StatusOK, lists) } diff --git a/internal/api/client/lists/listupdate.go b/internal/api/client/lists/listupdate.go new file mode 100644 index 000000000..80c5a8be3 --- /dev/null +++ b/internal/api/client/lists/listupdate.go @@ -0,0 +1,152 @@ +// 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 lists + +import ( + "errors" + "net/http" + "strings" + + "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" + "github.com/superseriousbusiness/gotosocial/internal/validate" +) + +// ListUpdatePUTHandler swagger:operation PUT /api/v1/list listUpdate +// +// Update an existing list. +// +// --- +// tags: +// - lists +// +// consumes: +// - application/json +// - application/xml +// - application/x-www-form-urlencoded +// +// produces: +// - application/json +// +// parameters: +// - +// name: id +// type: string +// description: ID of the list +// in: path +// required: true +// - +// name: title +// type: string +// description: Title of this list. +// in: formData +// example: Cool People +// - +// name: replies_policy +// type: string +// description: |- +// RepliesPolicy for this list. +// followed = Show replies to any followed user +// list = Show replies to members of the list +// none = Show replies to no one +// in: formData +// example: list +// +// security: +// - OAuth2 Bearer: +// - write:lists +// +// responses: +// '200': +// description: "The newly updated list." +// schema: +// "$ref": "#/definitions/list" +// '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) ListUpdatePUTHandler(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 + } + + 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 + } + + form := &apimodel.ListUpdateRequest{} + if err := c.ShouldBind(form); err != nil { + apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGetV1) + return + } + + if form.Title != nil { + if err := validate.ListTitle(*form.Title); err != nil { + apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGetV1) + return + } + } + + var repliesPolicy *gtsmodel.RepliesPolicy + if form.RepliesPolicy != nil { + rp := gtsmodel.RepliesPolicy(strings.ToLower(*form.RepliesPolicy)) + + if err := validate.ListRepliesPolicy(rp); err != nil { + apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGetV1) + return + } + + repliesPolicy = &rp + } + + if form.Title == nil && repliesPolicy == nil { + err = errors.New("neither title nor replies_policy was set; nothing to update") + apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGetV1) + return + } + + apiList, errWithCode := m.processor.List().Update(c.Request.Context(), authed.Account, targetListID, form.Title, repliesPolicy) + if errWithCode != nil { + apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1) + return + } + + c.JSON(http.StatusOK, apiList) +} |