package model import ( "encoding/json" "errors" "fmt" "io" "github.com/mattermost/focalboard/server/utils" "github.com/mattermost/mattermost-server/v6/shared/mlog" ) var ErrNoBoardsInBoardsAndBlocks = errors.New("at least one board is required") var ErrNoBlocksInBoardsAndBlocks = errors.New("at least one block is required") var ErrNoTeamInBoardsAndBlocks = errors.New("team ID cannot be empty") var ErrBoardIDsAndPatchesMissmatchInBoardsAndBlocks = errors.New("board ids and patches need to match") var ErrBlockIDsAndPatchesMissmatchInBoardsAndBlocks = errors.New("block ids and patches need to match") type BlockDoesntBelongToAnyBoardErr struct { blockID string } func (e BlockDoesntBelongToAnyBoardErr) Error() string { return fmt.Sprintf("block %s doesn't belong to any board", e.blockID) } // BoardsAndBlocks is used to operate over boards and blocks at the // same time // swagger:model type BoardsAndBlocks struct { // The boards // required: false Boards []*Board `json:"boards"` // The blocks // required: false Blocks []Block `json:"blocks"` } func (bab *BoardsAndBlocks) IsValid() error { if len(bab.Boards) == 0 { return ErrNoBoardsInBoardsAndBlocks } if len(bab.Blocks) == 0 { return ErrNoBlocksInBoardsAndBlocks } boardsMap := map[string]bool{} for _, board := range bab.Boards { boardsMap[board.ID] = true } for _, block := range bab.Blocks { if _, ok := boardsMap[block.BoardID]; !ok { return BlockDoesntBelongToAnyBoardErr{block.ID} } } return nil } // DeleteBoardsAndBlocks is used to list the boards and blocks to // delete on a request // swagger:model type DeleteBoardsAndBlocks struct { // The boards // required: true Boards []string `json:"boards"` // The blocks // required: true Blocks []string `json:"blocks"` } func NewDeleteBoardsAndBlocksFromBabs(babs *BoardsAndBlocks) *DeleteBoardsAndBlocks { boardIDs := make([]string, 0, len(babs.Boards)) blockIDs := make([]string, 0, len(babs.Boards)) for _, board := range babs.Boards { boardIDs = append(boardIDs, board.ID) } for _, block := range babs.Blocks { blockIDs = append(blockIDs, block.ID) } return &DeleteBoardsAndBlocks{ Boards: boardIDs, Blocks: blockIDs, } } func (dbab *DeleteBoardsAndBlocks) IsValid() error { if len(dbab.Boards) == 0 { return ErrNoBoardsInBoardsAndBlocks } if len(dbab.Blocks) == 0 { return ErrNoBlocksInBoardsAndBlocks } return nil } // PatchBoardsAndBlocks is used to patch multiple boards and blocks on // a single request // swagger:model type PatchBoardsAndBlocks struct { // The board IDs to patch // required: true BoardIDs []string `json:"boardIDs"` // The board patches // required: true BoardPatches []*BoardPatch `json:"boardPatches"` // The block IDs to patch // required: true BlockIDs []string `json:"blockIDs"` // The block patches // required: true BlockPatches []*BlockPatch `json:"blockPatches"` } func (dbab *PatchBoardsAndBlocks) IsValid() error { if len(dbab.BoardIDs) == 0 { return ErrNoBoardsInBoardsAndBlocks } if len(dbab.BoardIDs) != len(dbab.BoardPatches) { return ErrBoardIDsAndPatchesMissmatchInBoardsAndBlocks } if len(dbab.BlockIDs) == 0 { return ErrNoBlocksInBoardsAndBlocks } if len(dbab.BlockIDs) != len(dbab.BlockPatches) { return ErrBlockIDsAndPatchesMissmatchInBoardsAndBlocks } return nil } func GenerateBoardsAndBlocksIDs(bab *BoardsAndBlocks, logger *mlog.Logger) (*BoardsAndBlocks, error) { if err := bab.IsValid(); err != nil { return nil, err } blocksByBoard := map[string][]Block{} for _, block := range bab.Blocks { blocksByBoard[block.BoardID] = append(blocksByBoard[block.BoardID], block) } boards := []*Board{} blocks := []Block{} for _, board := range bab.Boards { newID := utils.NewID(utils.IDTypeBoard) for _, block := range blocksByBoard[board.ID] { block.BoardID = newID blocks = append(blocks, block) } board.ID = newID boards = append(boards, board) } newBab := &BoardsAndBlocks{ Boards: boards, Blocks: GenerateBlockIDs(blocks, logger), } return newBab, nil } func BoardsAndBlocksFromJSON(data io.Reader) *BoardsAndBlocks { var bab *BoardsAndBlocks _ = json.NewDecoder(data).Decode(&bab) return bab }