1
0
mirror of https://github.com/mattermost/focalboard.git synced 2025-10-31 00:17:42 +02:00
This commit is contained in:
Paul Esch-Laurent
2022-12-02 10:40:48 -06:00
parent f662e35daa
commit c4de1b9250
5 changed files with 339 additions and 4 deletions

View File

@@ -1,23 +1,37 @@
package api
import (
"encoding/json"
"fmt"
"net/http"
"strconv"
"github.com/gorilla/mux"
"github.com/mattermost/focalboard/server/model"
mmModel "github.com/mattermost/mattermost-server/v6/model"
"github.com/mattermost/mattermost-server/v6/shared/mlog"
)
const (
complianceDefaultPage = "0"
complianceDefaultPerPage = "60"
)
func (a *API) registerComplianceRoutes(r *mux.Router) {
// Compliance APIs
r.HandleFunc("/admin/boards", a.sessionRequired(a.handleGetAllBoards)).Methods("GET")
r.HandleFunc("/admin/boards_history", a.sessionRequired(a.handleGetBoardsHistory)).Methods("POST")
r.HandleFunc("/admin/boards_history", a.sessionRequired(a.handleGetBoardsHistory)).Methods("GET")
r.HandleFunc("/admin/blocks_history", a.sessionRequired(a.handleGetBlocksHistory)).Methods("GET")
}
func (a *API) handleGetAllBoards(w http.ResponseWriter, r *http.Request) {
// TODO(@pinjasaur): swagger
query := r.URL.Query()
teamID := query.Get("team_id")
strPage := query.Get("page")
strPerPage := query.Get("per_page")
// Valid authorization (`manage_system`)?
userID := getUserID(r)
if !a.permissions.HasPermissionTo(userID, mmModel.PermissionManageSystem) {
@@ -32,12 +46,78 @@ func (a *API) handleGetAllBoards(w http.ResponseWriter, r *http.Request) {
return
}
stringResponse(w, "OK")
if strPage == "" {
strPage = complianceDefaultPage
}
if strPerPage == "" {
strPerPage = complianceDefaultPerPage
}
page, err := strconv.Atoi(strPage)
if err != nil {
message := fmt.Sprintf("invalid `page` parameter: %s", err)
a.errorResponse(w, r, model.NewErrBadRequest(message))
return
}
perPage, err := strconv.Atoi(strPerPage)
if err != nil {
message := fmt.Sprintf("invalid `per_page` parameter: %s", err)
a.errorResponse(w, r, model.NewErrBadRequest(message))
return
}
boards, err := a.app.GetBoardsForTeam(teamID, page, perPage)
if err != nil {
a.errorResponse(w, r, err)
return
}
// N+1; is there more?
// TODO: potentially fragile if len(boards) == 0 or perPage < 0
hasNext := len(boards) > perPage
if hasNext {
boards = boards[:len(boards)-1]
}
a.logger.Debug("GetAllBoards",
mlog.String("teamID", teamID),
mlog.Int("boardsCount", len(boards)),
)
response := struct {
hasNext bool
results []*model.Board
}{
hasNext: hasNext,
results: boards,
}
// response := model.ComplianceResponse{
// HasNext: hasNext,
// Results: boards,
// }
data, err := json.Marshal(response)
if err != nil {
a.errorResponse(w, r, err)
return
}
jsonBytesResponse(w, http.StatusOK, data)
}
func (a *API) handleGetBoardsHistory(w http.ResponseWriter, r *http.Request) {
// TODO(@pinjasaur): swagger
query := r.URL.Query()
strModifiedSince := query.Get("modified_since") // required, everything else optional
includeDeleted := query.Get("include_deleted") == "true"
teamID := query.Get("team_id")
strPage := query.Get("page")
strPerPage := query.Get("per_page")
if strModifiedSince == "" {
a.errorResponse(w, r, model.NewErrBadRequest("`modified_since` parameter required"))
return
}
// Valid authorization (`manage_system`)?
userID := getUserID(r)
if !a.permissions.HasPermissionTo(userID, mmModel.PermissionManageSystem) {
@@ -52,12 +132,81 @@ func (a *API) handleGetBoardsHistory(w http.ResponseWriter, r *http.Request) {
return
}
stringResponse(w, "OK")
if strPage == "" {
strPage = complianceDefaultPage
}
if strPerPage == "" {
strPerPage = complianceDefaultPerPage
}
page, err := strconv.Atoi(strPage)
if err != nil {
message := fmt.Sprintf("invalid `page` parameter: %s", err)
a.errorResponse(w, r, model.NewErrBadRequest(message))
return
}
perPage, err := strconv.Atoi(strPerPage)
if err != nil {
message := fmt.Sprintf("invalid `per_page` parameter: %s", err)
a.errorResponse(w, r, model.NewErrBadRequest(message))
return
}
modifiedSince, err := strconv.Atoi(strModifiedSince)
if err != nil {
message := fmt.Sprintf("invalid `modified_since` parameter: %s", err)
a.errorResponse(w, r, model.NewErrBadRequest(message))
return
}
boards, err := a.app.GetBoardsHistory(modifiedSince, includeDeleted, teamID, page, perPage)
if err != nil {
a.errorResponse(w, r, err)
return
}
// N+1; is there more?
// TODO: potentially fragile if len(boards) == 0 or perPage < 0
hasNext := len(boards) > perPage
if hasNext {
boards = boards[:len(boards)-1]
}
a.logger.Debug("GetBoardsHistory",
mlog.String("teamID", teamID),
mlog.Int("boardsCount", len(boards)),
)
response := struct {
hasNext bool
results []*model.Board // ish
}{
hasNext: hasNext,
results: boards,
}
data, err := json.Marshal(response)
if err != nil {
a.errorResponse(w, r, err)
return
}
jsonBytesResponse(w, http.StatusOK, data)
}
func (a *API) handleGetBlocksHistory(w http.ResponseWriter, r *http.Request) {
// TODO(@pinjasaur): swagger
query := r.URL.Query()
strModifiedSince := query.Get("modified_since") // required, everything else optional
includeDeleted := query.Get("include_deleted") == "true"
teamID := query.Get("team_id")
boardID := query.Get("board_id")
strPage := query.Get("page")
strPerPage := query.Get("per_page")
if strModifiedSince == "" {
a.errorResponse(w, r, model.NewErrBadRequest("`modified_since` parameter required"))
return
}
// Valid authorization (`manage_system`)?
userID := getUserID(r)
if !a.permissions.HasPermissionTo(userID, mmModel.PermissionManageSystem) {
@@ -72,5 +221,62 @@ func (a *API) handleGetBlocksHistory(w http.ResponseWriter, r *http.Request) {
return
}
stringResponse(w, "OK")
if strPage == "" {
strPage = complianceDefaultPage
}
if strPerPage == "" {
strPerPage = complianceDefaultPerPage
}
page, err := strconv.Atoi(strPage)
if err != nil {
message := fmt.Sprintf("invalid `page` parameter: %s", err)
a.errorResponse(w, r, model.NewErrBadRequest(message))
return
}
perPage, err := strconv.Atoi(strPerPage)
if err != nil {
message := fmt.Sprintf("invalid `per_page` parameter: %s", err)
a.errorResponse(w, r, model.NewErrBadRequest(message))
return
}
modifiedSince, err := strconv.Atoi(strModifiedSince)
if err != nil {
message := fmt.Sprintf("invalid `modified_since` parameter: %s", err)
a.errorResponse(w, r, model.NewErrBadRequest(message))
return
}
blocks, err := a.app.GetBlocksHistory(modifiedSince, includeDeleted, teamID, boardID, page, perPage)
if err != nil {
a.errorResponse(w, r, err)
return
}
// N+1; is there more?
// TODO: potentially fragile if len(blocks) == 0 or perPage < 0
hasNext := len(blocks) > perPage
if hasNext {
blocks = blocks[:len(blocks)-1]
}
a.logger.Debug("GetBlocksHistory",
mlog.String("teamID", teamID),
mlog.String("boardID", boardID),
mlog.Int("blocksCount", len(blocks)),
)
response := struct {
hasNext bool
results []*model.Block // ish
}{
hasNext: hasNext,
results: blocks,
}
data, err := json.Marshal(response)
if err != nil {
a.errorResponse(w, r, err)
return
}
jsonBytesResponse(w, http.StatusOK, data)
}

View File

@@ -256,6 +256,18 @@ func (a *App) DuplicateBoard(boardID, userID, toTeam string, asTemplate bool) (*
return bab, members, err
}
func (a *App) GetBoardsForTeam(teamID string, page, perPage int) ([]*model.Board, error) {
return a.store.GetBoardsForTeam(teamID, page, perPage)
}
func (a *App) GetBoardsHistory(modifiedSince int, includeDeleted bool, teamID string, page, perPage int) ([]*model.Board, error) {
return a.store.GetBoardsHistory(modifiedSince, includeDeleted, teamID, page, perPage)
}
func (a *App) GetBlocksHistory(modifiedSince int, includeDeleted bool, teamID, boardID string, page, perPage int) ([]*model.Block, error) {
return a.store.GetBlocksHistory(modifiedSince, includeDeleted, teamID, boardID, page, perPage)
}
func (a *App) GetBoardsForUserAndTeam(userID, teamID string, includePublicBoards bool) ([]*model.Board, error) {
return a.store.GetBoardsForUserAndTeam(userID, teamID, includePublicBoards)
}

View File

@@ -0,0 +1,15 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package model
// ComplianceResponse is the generic response body to a compliance API
// swagger:model
type ComplianceResponse struct {
// Is there a next page for pagination?
// required: true
HasNext bool `json:"hasNext"`
// The array of results
// required: true
Results []interface{} `json:"results"`
}

View File

@@ -1041,6 +1041,105 @@ func (s *MattermostAuthLayer) GetMembersForBoard(boardID string) ([]*model.Board
return members, nil
}
func (s *MattermostAuthLayer) GetBoardsForTeam(teamID string, page, perPage int) ([]*model.Board, error) {
query := s.getQueryBuilder().
Select(boardFields("b.")...).
From(s.tablePrefix + "boards as b")
if teamID != "" {
query = query.Where(sq.Eq{"b.team_id": teamID})
}
if page != 0 {
query = query.Offset(uint64(page * perPage))
}
if perPage > 0 {
// N+1 to check if there's a next page for pagination
query = query.Limit(uint64(perPage) + 1)
}
rows, err := query.Query()
if err != nil {
s.logger.Error(`GetBoardsForTeam ERROR`, mlog.Err(err))
return nil, err
}
defer s.CloseRows(rows)
return s.boardsFromRows(rows, true)
}
func (s *MattermostAuthLayer) GetBoardsHistory(modifiedSince int, includeDeleted bool, teamID string, page, perPage int) ([]*model.Board, error) {
query := s.getQueryBuilder().
Select("*"). // TODO: specify
From(s.tablePrefix + "boards_history as bh").
Where(sq.Gt{"bh.update_at": modifiedSince})
if !includeDeleted {
query = query.Where(sq.Eq{"bh.delete_at": 0})
}
if teamID != "" {
query = query.Where(sq.Eq{"bh.team_id": teamID})
}
if page != 0 {
query = query.Offset(uint64(page * perPage))
}
if perPage > 0 {
// N+1 to check if there's a next page for pagination
query = query.Limit(uint64(perPage) + 1)
}
rows, err := query.Query()
if err != nil {
s.logger.Error(`GetBoardsHistory ERROR`, mlog.Err(err))
return nil, err
}
defer s.CloseRows(rows)
return s.boardsFromRows(rows, true)
}
func (s *MattermostAuthLayer) GetBlocksHistory(modifiedSince int, includeDeleted bool, teamID, boardID string, page, perPage int) ([]*model.Block, error) {
query := s.getQueryBuilder().
Select("*"). // TODO: specify
From(s.tablePrefix + "blocks_history as bh").
Where(sq.Gt{"bh.update_at": modifiedSince})
if !includeDeleted {
query = query.Where(sq.Eq{"bh.delete_at": 0})
}
if teamID != "" {
query = query.Where(sq.Eq{"bh.team_id": teamID})
}
if boardID != "" {
query = query.Where(sq.Eq{"bh.board_id": boardID})
}
if page != 0 {
query = query.Offset(uint64(page * perPage))
}
if perPage > 0 {
// N+1 to check if there's a next page for pagination
query = query.Limit(uint64(perPage) + 1)
}
rows, err := query.Query()
if err != nil {
s.logger.Error(`GetBoardsHistory ERROR`, mlog.Err(err))
return nil, err
}
defer s.CloseRows(rows)
// TODO: copy implementation from SQLStore?
return s.blocksFromRows(rows)
}
func (s *MattermostAuthLayer) GetBoardsForUserAndTeam(userID, teamID string, includePublicBoards bool) ([]*model.Board, error) {
if includePublicBoards {
boards, err := s.SearchBoardsForUserInTeam(teamID, "", userID)

View File

@@ -92,6 +92,9 @@ type Store interface {
// @withTransaction
PatchBoard(boardID string, boardPatch *model.BoardPatch, userID string) (*model.Board, error)
GetBoard(id string) (*model.Board, error)
GetBoardsForTeam(teamID string, page, perPage int) ([]*model.Board, error)
GetBoardsHistory(modifiedSince int, includeDeleted bool, teamID string, page, perPage int) ([]*model.Board, error)
GetBlocksHistory(modifiedSince int, includeDeleted bool, teamID, boardID string, page, perPage int) ([]*model.Block, error)
GetBoardsForUserAndTeam(userID, teamID string, includePublicBoards bool) ([]*model.Board, error)
GetBoardsInTeamByIds(boardIDs []string, teamID string) ([]*model.Board, error)
// @withTransaction