client.go
TLDR
This file contains the implementation of a client for interacting with an API. It includes methods for making API requests, handling errors, and performing various actions such as creating boards, cards, and categories, as well as getting information about teams, users, and subscriptions.
Methods
BuildResponse
Builds a response object from an http response.
BuildErrorResponse
Builds an error response object from an http response and an error.
closeBody
Closes the body of an http response.
toJSON
Converts a value to its JSON representation.
NewClient
Creates a new client object with the specified URL and session token.
DoAPIGet
Performs an API GET request to the specified URL.
DoAPIPost
Performs an API POST request to the specified URL with the specified data.
DoAPIPatch
Performs an API PATCH request to the specified URL with the specified data.
DoAPIPut
Performs an API PUT request to the specified URL with the specified data.
DoAPIDelete
Performs an API DELETE request to the specified URL with the specified data.
DoAPIRequest
Performs an API request with the specified method, URL, data, and etag.
GetTeamRoute
Returns the route for getting a team.
GetTeamsRoute
Returns the route for getting teams.
GetBlockRoute
Returns the route for getting a block.
GetBoardsRoute
Returns the route for getting boards.
GetBoardRoute
Returns the route for getting a board.
GetBoardMetadataRoute
Returns the route for getting board metadata.
GetJoinBoardRoute
Returns the route for joining a board.
GetLeaveBoardRoute
Returns the route for leaving a board.
GetBlocksRoute
Returns the route for getting blocks.
GetAllBlocksRoute
Returns the route for getting all blocks.
GetBoardsAndBlocksRoute
Returns the route for getting boards and blocks.
GetCardsRoute
Returns the route for getting cards.
GetCardRoute
Returns the route for getting a card.
GetTeam
Gets a team.
GetTeamBoardsInsights
Gets board insights for a team.
GetUserBoardsInsights
Gets board insights for a user.
GetBlocksForBoard
Gets blocks for a board.
GetAllBlocksForBoard
Gets all blocks for a board.
PatchBlock
Patches a block with the specified board ID, block ID, and block patch.
DuplicateBoard
Duplicates a board.
DuplicateBlock
Duplicates a block with the specified board ID and block ID.
UndeleteBlock
Undeletes a block with the specified board ID and block ID.
InsertBlocks
Inserts blocks with the specified board ID and blocks.
DeleteBlock
Deletes a block with the specified board ID and block ID.
CreateCard
Creates a card with the specified board ID and card.
GetCards
Gets cards for a board.
PatchCard
Patches a card with the specified card ID and card patch.
GetCard
Gets a card with the specified card ID.
CreateBoardsAndBlocks
Creates boards and blocks.
CreateCategory
Creates a category.
DeleteCategory
Deletes a category.
UpdateCategoryBoard
Updates the board for a category.
GetUserCategoryBoards
Gets category boards for a user.
ReorderCategories
Reorders categories.
ReorderCategoryBoards
Reorders category boards.
PatchBoardsAndBlocks
Patches boards and blocks.
DeleteBoardsAndBlocks
Deletes boards and blocks.
GetSharingRoute
Returns the route for getting sharing details.
GetSharing
Gets sharing details for a board.
PostSharing
Posts sharing details for a board.
GetRegisterRoute
Returns the route for registration.
Register
Registers a user.
GetLoginRoute
Returns the route for login.
Login
Logs in a user.
GetMeRoute
Returns the route for getting the current user.
GetMe
Gets the current user.
GetUserID
Gets the ID of the current user.
GetUserRoute
Returns the route for getting a user.
GetUser
Gets a user with the specified ID.
GetUserList
Gets a list of users with the specified IDs.
GetUserChangePasswordRoute
Returns the route for changing a user's password.
UserChangePassword
Changes a user's password with the specified ID and request data.
CreateBoard
Creates a board with the specified board.
PatchBoard
Patches a board with the specified board ID and patch.
DeleteBoard
Deletes a board with the specified board ID.
UndeleteBoard
Undeletes a board with the specified board ID.
GetBoard
Gets a board with the specified board ID.
GetBoardMetadata
Gets the metadata for a board with the specified board ID.
GetBoardsForTeam
Gets boards for a team.
SearchBoardsForUser
Searches for boards for a user in a team.
SearchBoardsForTeam
Searches for boards in a team.
GetMembersForBoard
Gets members for a board.
AddMemberToBoard
Adds a member to a board.
JoinBoard
Joins a board with the specified board ID.
LeaveBoard
Leaves a board with the specified board ID.
UpdateBoardMember
Updates a member of a board.
DeleteBoardMember
Deletes a member of a board.
TeamUploadFile
Uploads a file to a team board.
TeamUploadFileInfo
Gets information about an uploaded file.
CreateSubscription
Creates a subscription.
DeleteSubscription
Deletes a subscription.
GetSubscriptions
Gets subscriptions for a subscriber.
GetTemplatesForTeam
Gets templates for a team.
ExportBoardArchive
Exports the archive of a board.
ImportArchive
Imports an archive.
GetLimits
Gets the limits for Boards Cloud.
MoveContentBlock
Moves a content block.
GetStatistics
Gets statistics for Boards Cloud.
GetBoardsForCompliance
Gets boards for compliance.
GetBoardsComplianceHistory
Gets compliance history for boards.
GetBlocksComplianceHistory
Gets compliance history for blocks.
HideBoard
Hides a board.
UnhideBoard
Unhides a board.
Classes
None
package client
import (
"bytes"
"encoding/json"
"fmt"
"io"
"mime/multipart"
"net/http"
"strings"
"github.com/mattermost/focalboard/server/api"
"github.com/mattermost/focalboard/server/model"
mmModel "github.com/mattermost/mattermost-server/v6/model"
)
const (
APIURLSuffix = "/api/v2"
)
type RequestReaderError struct {
buf []byte
}
func (rre RequestReaderError) Error() string {
return "payload: " + string(rre.buf)
}
type Response struct {
StatusCode int
Error error
Header http.Header
}
func BuildResponse(r *http.Response) *Response {
return &Response{
StatusCode: r.StatusCode,
Header: r.Header,
}
}
func BuildErrorResponse(r *http.Response, err error) *Response {
statusCode := 0
header := make(http.Header)
if r != nil {
statusCode = r.StatusCode
header = r.Header
}
return &Response{
StatusCode: statusCode,
Error: err,
Header: header,
}
}
func closeBody(r *http.Response) {
if r.Body != nil {
_, _ = io.Copy(io.Discard, r.Body)
_ = r.Body.Close()
}
}
func toJSON(v interface{}) string {
b, _ := json.Marshal(v)
return string(b)
}
type Client struct {
URL string
APIURL string
HTTPClient *http.Client
HTTPHeader map[string]string
// Token if token is empty indicate client is not login yet
Token string
}
func NewClient(url, sessionToken string) *Client {
url = strings.TrimRight(url, "/")
headers := map[string]string{
"X-Requested-With": "XMLHttpRequest",
}
return &Client{url, url + APIURLSuffix, &http.Client{}, headers, sessionToken}
}
func (c *Client) DoAPIGet(url, etag string) (*http.Response, error) {
return c.DoAPIRequest(http.MethodGet, c.APIURL+url, "", etag)
}
func (c *Client) DoAPIPost(url, data string) (*http.Response, error) {
return c.DoAPIRequest(http.MethodPost, c.APIURL+url, data, "")
}
func (c *Client) DoAPIPatch(url, data string) (*http.Response, error) {
return c.DoAPIRequest(http.MethodPatch, c.APIURL+url, data, "")
}
func (c *Client) DoAPIPut(url, data string) (*http.Response, error) {
return c.DoAPIRequest(http.MethodPut, c.APIURL+url, data, "")
}
func (c *Client) DoAPIDelete(url string, data string) (*http.Response, error) {
return c.DoAPIRequest(http.MethodDelete, c.APIURL+url, data, "")
}
func (c *Client) DoAPIRequest(method, url, data, etag string) (*http.Response, error) {
return c.doAPIRequestReader(method, url, strings.NewReader(data), etag)
}
type requestOption func(r *http.Request)
func (c *Client) doAPIRequestReader(method, url string, data io.Reader, _ /* etag */ string, opts ...requestOption) (*http.Response, error) {
rq, err := http.NewRequest(method, url, data)
if err != nil {
return nil, err
}
for _, opt := range opts {
opt(rq)
}
if c.HTTPHeader != nil && len(c.HTTPHeader) > 0 {
for k, v := range c.HTTPHeader {
rq.Header.Set(k, v)
}
}
if c.Token != "" {
rq.Header.Set("Authorization", "Bearer "+c.Token)
}
rp, err := c.HTTPClient.Do(rq)
if err != nil || rp == nil {
return nil, err
}
if rp.StatusCode == http.StatusNotModified {
return rp, nil
}
if rp.StatusCode >= http.StatusMultipleChoices {
defer closeBody(rp)
b, err := io.ReadAll(rp.Body)
if err != nil {
return rp, fmt.Errorf("error when parsing response with code %d: %w", rp.StatusCode, err)
}
return rp, RequestReaderError{b}
}
return rp, nil
}
func (c *Client) GetTeamRoute(teamID string) string {
return fmt.Sprintf("%s/%s", c.GetTeamsRoute(), teamID)
}
func (c *Client) GetTeamsRoute() string {
return "/teams"
}
func (c *Client) GetBlockRoute(boardID, blockID string) string {
return fmt.Sprintf("%s/%s", c.GetBlocksRoute(boardID), blockID)
}
func (c *Client) GetBoardsRoute() string {
return "/boards"
}
func (c *Client) GetBoardRoute(boardID string) string {
return fmt.Sprintf("%s/%s", c.GetBoardsRoute(), boardID)
}
func (c *Client) GetBoardMetadataRoute(boardID string) string {
return fmt.Sprintf("%s/%s/metadata", c.GetBoardsRoute(), boardID)
}
func (c *Client) GetJoinBoardRoute(boardID string) string {
return fmt.Sprintf("%s/%s/join", c.GetBoardsRoute(), boardID)
}
func (c *Client) GetLeaveBoardRoute(boardID string) string {
return fmt.Sprintf("%s/%s/join", c.GetBoardsRoute(), boardID)
}
func (c *Client) GetBlocksRoute(boardID string) string {
return fmt.Sprintf("%s/blocks", c.GetBoardRoute(boardID))
}
func (c *Client) GetAllBlocksRoute(boardID string) string {
return fmt.Sprintf("%s/blocks?all=true", c.GetBoardRoute(boardID))
}
func (c *Client) GetBoardsAndBlocksRoute() string {
return "/boards-and-blocks"
}
func (c *Client) GetCardsRoute() string {
return "/cards"
}
func (c *Client) GetCardRoute(cardID string) string {
return fmt.Sprintf("%s/%s", c.GetCardsRoute(), cardID)
}
func (c *Client) GetTeam(teamID string) (*model.Team, *Response) {
r, err := c.DoAPIGet(c.GetTeamRoute(teamID), "")
if err != nil {
return nil, BuildErrorResponse(r, err)
}
defer closeBody(r)
return model.TeamFromJSON(r.Body), BuildResponse(r)
}
func (c *Client) GetTeamBoardsInsights(teamID string, userID string, timeRange string, page int, perPage int) (*model.BoardInsightsList, *Response) {
query := fmt.Sprintf("?time_range=%v&page=%v&per_page=%v", timeRange, page, perPage)
r, err := c.DoAPIGet(c.GetTeamRoute(teamID)+"/boards/insights"+query, "")
if err != nil {
return nil, BuildErrorResponse(r, err)
}
defer closeBody(r)
var boardInsightsList *model.BoardInsightsList
if jsonErr := json.NewDecoder(r.Body).Decode(&boardInsightsList); jsonErr != nil {
return nil, BuildErrorResponse(r, jsonErr)
}
return boardInsightsList, BuildResponse(r)
}
func (c *Client) GetUserBoardsInsights(teamID string, userID string, timeRange string, page int, perPage int) (*model.BoardInsightsList, *Response) {
query := fmt.Sprintf("?time_range=%v&page=%v&per_page=%v&team_id=%v", timeRange, page, perPage, teamID)
r, err := c.DoAPIGet(c.GetMeRoute()+"/boards/insights"+query, "")
if err != nil {
return nil, BuildErrorResponse(r, err)
}
defer closeBody(r)
var boardInsightsList *model.BoardInsightsList
if jsonErr := json.NewDecoder(r.Body).Decode(&boardInsightsList); jsonErr != nil {
return nil, BuildErrorResponse(r, jsonErr)
}
return boardInsightsList, BuildResponse(r)
}
func (c *Client) GetBlocksForBoard(boardID string) ([]*model.Block, *Response) {
r, err := c.DoAPIGet(c.GetBlocksRoute(boardID), "")
if err != nil {
return nil, BuildErrorResponse(r, err)
}
defer closeBody(r)
return model.BlocksFromJSON(r.Body), BuildResponse(r)
}
func (c *Client) GetAllBlocksForBoard(boardID string) ([]*model.Block, *Response) {
r, err := c.DoAPIGet(c.GetAllBlocksRoute(boardID), "")
if err != nil {
return nil, BuildErrorResponse(r, err)
}
defer closeBody(r)
return model.BlocksFromJSON(r.Body), BuildResponse(r)
}
const disableNotifyQueryParam = "disable_notify=true"
func (c *Client) PatchBlock(boardID, blockID string, blockPatch *model.BlockPatch, disableNotify bool) (bool, *Response) {
var queryParams string
if disableNotify {
queryParams = "?" + disableNotifyQueryParam
}
r, err := c.DoAPIPatch(c.GetBlockRoute(boardID, blockID)+queryParams, toJSON(blockPatch))
if err != nil {
return false, BuildErrorResponse(r, err)
}
defer closeBody(r)
return true, BuildResponse(r)
}
func (c *Client) DuplicateBoard(boardID string, asTemplate bool, teamID string) (*model.BoardsAndBlocks, *Response) {
queryParams := "?asTemplate=false&"
if asTemplate {
queryParams = "?asTemplate=true"
}
if len(teamID) > 0 {
queryParams = queryParams + "&toTeam=" + teamID
}
r, err := c.DoAPIPost(c.GetBoardRoute(boardID)+"/duplicate"+queryParams, "")
if err != nil {
return nil, BuildErrorResponse(r, err)
}
defer closeBody(r)
return model.BoardsAndBlocksFromJSON(r.Body), BuildResponse(r)
}
func (c *Client) DuplicateBlock(boardID, blockID string, asTemplate bool) (bool, *Response) {
queryParams := "?asTemplate=false"
if asTemplate {
queryParams = "?asTemplate=true"
}
r, err := c.DoAPIPost(c.GetBlockRoute(boardID, blockID)+"/duplicate"+queryParams, "")
if err != nil {
return false, BuildErrorResponse(r, err)
}
defer closeBody(r)
return true, BuildResponse(r)
}
func (c *Client) UndeleteBlock(boardID, blockID string) (bool, *Response) {
r, err := c.DoAPIPost(c.GetBlockRoute(boardID, blockID)+"/undelete", "")
if err != nil {
return false, BuildErrorResponse(r, err)
}
defer closeBody(r)
return true, BuildResponse(r)
}
func (c *Client) InsertBlocks(boardID string, blocks []*model.Block, disableNotify bool) ([]*model.Block, *Response) {
var queryParams string
if disableNotify {
queryParams = "?" + disableNotifyQueryParam
}
r, err := c.DoAPIPost(c.GetBlocksRoute(boardID)+queryParams, toJSON(blocks))
if err != nil {
return nil, BuildErrorResponse(r, err)
}
defer closeBody(r)
return model.BlocksFromJSON(r.Body), BuildResponse(r)
}
func (c *Client) DeleteBlock(boardID, blockID string, disableNotify bool) (bool, *Response) {
var queryParams string
if disableNotify {
queryParams = "?" + disableNotifyQueryParam
}
r, err := c.DoAPIDelete(c.GetBlockRoute(boardID, blockID)+queryParams, "")
if err != nil {
return false, BuildErrorResponse(r, err)
}
defer closeBody(r)
return true, BuildResponse(r)
}
//
// Cards
//
func (c *Client) CreateCard(boardID string, card *model.Card, disableNotify bool) (*model.Card, *Response) {
var queryParams string
if disableNotify {
queryParams = "?" + disableNotifyQueryParam
}
r, err := c.DoAPIPost(c.GetBoardRoute(boardID)+"/cards"+queryParams, toJSON(card))
if err != nil {
return nil, BuildErrorResponse(r, err)
}
defer closeBody(r)
var cardNew *model.Card
if err := json.NewDecoder(r.Body).Decode(&cardNew); err != nil {
return nil, BuildErrorResponse(r, err)
}
return cardNew, BuildResponse(r)
}
func (c *Client) GetCards(boardID string, page int, perPage int) ([]*model.Card, *Response) {
url := fmt.Sprintf("%s/cards?page=%d&per_page=%d", c.GetBoardRoute(boardID), page, perPage)
r, err := c.DoAPIGet(url, "")
if err != nil {
return nil, BuildErrorResponse(r, err)
}
defer closeBody(r)
var cards []*model.Card
if err := json.NewDecoder(r.Body).Decode(&cards); err != nil {
return nil, BuildErrorResponse(r, err)
}
return cards, BuildResponse(r)
}
func (c *Client) PatchCard(cardID string, cardPatch *model.CardPatch, disableNotify bool) (*model.Card, *Response) {
var queryParams string
if disableNotify {
queryParams = "?" + disableNotifyQueryParam
}
r, err := c.DoAPIPatch(c.GetCardRoute(cardID)+queryParams, toJSON(cardPatch))
if err != nil {
return nil, BuildErrorResponse(r, err)
}
defer closeBody(r)
var cardNew *model.Card
if err := json.NewDecoder(r.Body).Decode(&cardNew); err != nil {
return nil, BuildErrorResponse(r, err)
}
return cardNew, BuildResponse(r)
}
func (c *Client) GetCard(cardID string) (*model.Card, *Response) {
r, err := c.DoAPIGet(c.GetCardRoute(cardID), "")
if err != nil {
return nil, BuildErrorResponse(r, err)
}
defer closeBody(r)
var card *model.Card
if err := json.NewDecoder(r.Body).Decode(&card); err != nil {
return nil, BuildErrorResponse(r, err)
}
return card, BuildResponse(r)
}
//
// Boards and blocks.
//
func (c *Client) CreateBoardsAndBlocks(bab *model.BoardsAndBlocks) (*model.BoardsAndBlocks, *Response) {
r, err := c.DoAPIPost(c.GetBoardsAndBlocksRoute(), toJSON(bab))
if err != nil {
return nil, BuildErrorResponse(r, err)
}
defer closeBody(r)
return model.BoardsAndBlocksFromJSON(r.Body), BuildResponse(r)
}
func (c *Client) CreateCategory(category model.Category) (*model.Category, *Response) {
r, err := c.DoAPIPost(c.GetTeamRoute(category.TeamID)+"/categories", toJSON(category))
if err != nil {
return nil, BuildErrorResponse(r, err)
}
defer closeBody(r)
return model.CategoryFromJSON(r.Body), BuildResponse(r)
}
func (c *Client) DeleteCategory(teamID, categoryID string) *Response {
r, err := c.DoAPIDelete(c.GetTeamRoute(teamID)+"/categories/"+categoryID, "")
if err != nil {
return BuildErrorResponse(r, err)
}
defer closeBody(r)
return BuildResponse(r)
}
func (c *Client) UpdateCategoryBoard(teamID, categoryID, boardID string) *Response {
r, err := c.DoAPIPost(fmt.Sprintf("%s/categories/%s/boards/%s", c.GetTeamRoute(teamID), categoryID, boardID), "")
if err != nil {
return BuildErrorResponse(r, err)
}
defer closeBody(r)
return BuildResponse(r)
}
func (c *Client) GetUserCategoryBoards(teamID string) ([]model.CategoryBoards, *Response) {
r, err := c.DoAPIGet(c.GetTeamRoute(teamID)+"/categories", "")
if err != nil {
return nil, BuildErrorResponse(r, err)
}
defer closeBody(r)
var categoryBoards []model.CategoryBoards
_ = json.NewDecoder(r.Body).Decode(&categoryBoards)
return categoryBoards, BuildResponse(r)
}
func (c *Client) ReorderCategories(teamID string, newOrder []string) ([]string, *Response) {
r, err := c.DoAPIPut(c.GetTeamRoute(teamID)+"/categories/reorder", toJSON(newOrder))
if err != nil {
return nil, BuildErrorResponse(r, err)
}
defer closeBody(r)
var updatedCategoryOrder []string
_ = json.NewDecoder(r.Body).Decode(&updatedCategoryOrder)
return updatedCategoryOrder, BuildResponse(r)
}
func (c *Client) ReorderCategoryBoards(teamID, categoryID string, newOrder []string) ([]string, *Response) {
r, err := c.DoAPIPut(c.GetTeamRoute(teamID)+"/categories/"+categoryID+"/reorder", toJSON(newOrder))
if err != nil {
return nil, BuildErrorResponse(r, err)
}
defer closeBody(r)
var updatedBoardsOrder []string
_ = json.NewDecoder(r.Body).Decode(&updatedBoardsOrder)
return updatedBoardsOrder, BuildResponse(r)
}
func (c *Client) PatchBoardsAndBlocks(pbab *model.PatchBoardsAndBlocks) (*model.BoardsAndBlocks, *Response) {
r, err := c.DoAPIPatch(c.GetBoardsAndBlocksRoute(), toJSON(pbab))
if err != nil {
return nil, BuildErrorResponse(r, err)
}
defer closeBody(r)
return model.BoardsAndBlocksFromJSON(r.Body), BuildResponse(r)
}
func (c *Client) DeleteBoardsAndBlocks(dbab *model.DeleteBoardsAndBlocks) (bool, *Response) {
r, err := c.DoAPIDelete(c.GetBoardsAndBlocksRoute(), toJSON(dbab))
if err != nil {
return false, BuildErrorResponse(r, err)
}
defer closeBody(r)
return true, BuildResponse(r)
}
// Sharing
func (c *Client) GetSharingRoute(boardID string) string {
return fmt.Sprintf("%s/sharing", c.GetBoardRoute(boardID))
}
func (c *Client) GetSharing(boardID string) (*model.Sharing, *Response) {
r, err := c.DoAPIGet(c.GetSharingRoute(boardID), "")
if err != nil {
return nil, BuildErrorResponse(r, err)
}
defer closeBody(r)
sharing := model.SharingFromJSON(r.Body)
return &sharing, BuildResponse(r)
}
func (c *Client) PostSharing(sharing *model.Sharing) (bool, *Response) {
r, err := c.DoAPIPost(c.GetSharingRoute(sharing.ID), toJSON(sharing))
if err != nil {
return false, BuildErrorResponse(r, err)
}
defer closeBody(r)
return true, BuildResponse(r)
}
func (c *Client) GetRegisterRoute() string {
return "/register"
}
func (c *Client) Register(request *model.RegisterRequest) (bool, *Response) {
r, err := c.DoAPIPost(c.GetRegisterRoute(), toJSON(&request))
if err != nil {
return false, BuildErrorResponse(r, err)
}
defer closeBody(r)
return true, BuildResponse(r)
}
func (c *Client) GetLoginRoute() string {
return "/login"
}
func (c *Client) Login(request *model.LoginRequest) (*model.LoginResponse, *Response) {
r, err := c.DoAPIPost(c.GetLoginRoute(), toJSON(&request))
if err != nil {
return nil, BuildErrorResponse(r, err)
}
defer closeBody(r)
data, err := model.LoginResponseFromJSON(r.Body)
if err != nil {
return nil, BuildErrorResponse(r, err)
}
if data.Token != "" {
c.Token = data.Token
}
return data, BuildResponse(r)
}
func (c *Client) GetMeRoute() string {
return "/users/me"
}
func (c *Client) GetMe() (*model.User, *Response) {
r, err := c.DoAPIGet(c.GetMeRoute(), "")
if err != nil {
return nil, BuildErrorResponse(r, err)
}
defer closeBody(r)
me, err := model.UserFromJSON(r.Body)
if err != nil {
return nil, BuildErrorResponse(r, err)
}
return me, BuildResponse(r)
}
func (c *Client) GetUserID() string {
me, _ := c.GetMe()
if me == nil {
return ""
}
return me.ID
}
func (c *Client) GetUserRoute(id string) string {
return fmt.Sprintf("/users/%s", id)
}
func (c *Client) GetUser(id string) (*model.User, *Response) {
r, err := c.DoAPIGet(c.GetUserRoute(id), "")
if err != nil {
return nil, BuildErrorResponse(r, err)
}
defer closeBody(r)
user, err := model.UserFromJSON(r.Body)
if err != nil {
return nil, BuildErrorResponse(r, err)
}
return user, BuildResponse(r)
}
func (c *Client) GetUserList(ids []string) ([]model.User, *Response) {
r, err := c.DoAPIPost("/users", toJSON(ids))
if err != nil {
return nil, BuildErrorResponse(r, err)
}
defer closeBody(r)
requestBody, err := io.ReadAll(r.Body)
if err != nil {
return nil, BuildErrorResponse(r, err)
}
var users []model.User
err = json.Unmarshal(requestBody, &users)
if err != nil {
return nil, BuildErrorResponse(r, err)
}
return users, BuildResponse(r)
}
func (c *Client) GetUserChangePasswordRoute(id string) string {
return fmt.Sprintf("/users/%s/changepassword", id)
}
func (c *Client) UserChangePassword(id string, data *model.ChangePasswordRequest) (bool, *Response) {
r, err := c.DoAPIPost(c.GetUserChangePasswordRoute(id), toJSON(&data))
if err != nil {
return false, BuildErrorResponse(r, err)
}
defer closeBody(r)
return true, BuildResponse(r)
}
func (c *Client) CreateBoard(board *model.Board) (*model.Board, *Response) {
r, err := c.DoAPIPost(c.GetBoardsRoute(), toJSON(board))
if err != nil {
return nil, BuildErrorResponse(r, err)
}
defer closeBody(r)
return model.BoardFromJSON(r.Body), BuildResponse(r)
}
func (c *Client) PatchBoard(boardID string, patch *model.BoardPatch) (*model.Board, *Response) {
r, err := c.DoAPIPatch(c.GetBoardRoute(boardID), toJSON(patch))
if err != nil {
return nil, BuildErrorResponse(r, err)
}
defer closeBody(r)
return model.BoardFromJSON(r.Body), BuildResponse(r)
}
func (c *Client) DeleteBoard(boardID string) (bool, *Response) {
r, err := c.DoAPIDelete(c.GetBoardRoute(boardID), "")
if err != nil {
return false, BuildErrorResponse(r, err)
}
defer closeBody(r)
return true, BuildResponse(r)
}
func (c *Client) UndeleteBoard(boardID string) (bool, *Response) {
r, err := c.DoAPIPost(c.GetBoardRoute(boardID)+"/undelete", "")
if err != nil {
return false, BuildErrorResponse(r, err)
}
defer closeBody(r)
return true, BuildResponse(r)
}
func (c *Client) GetBoard(boardID, readToken string) (*model.Board, *Response) {
url := c.GetBoardRoute(boardID)
if readToken != "" {
url += fmt.Sprintf("?read_token=%s", readToken)
}
r, err := c.DoAPIGet(url, "")
if err != nil {
return nil, BuildErrorResponse(r, err)
}
defer closeBody(r)
return model.BoardFromJSON(r.Body), BuildResponse(r)
}
func (c *Client) GetBoardMetadata(boardID, readToken string) (*model.BoardMetadata, *Response) {
url := c.GetBoardMetadataRoute(boardID)
if readToken != "" {
url += fmt.Sprintf("?read_token=%s", readToken)
}
r, err := c.DoAPIGet(url, "")
if err != nil {
return nil, BuildErrorResponse(r, err)
}
defer closeBody(r)
return model.BoardMetadataFromJSON(r.Body), BuildResponse(r)
}
func (c *Client) GetBoardsForTeam(teamID string) ([]*model.Board, *Response) {
r, err := c.DoAPIGet(c.GetTeamRoute(teamID)+"/boards", "")
if err != nil {
return nil, BuildErrorResponse(r, err)
}
defer closeBody(r)
return model.BoardsFromJSON(r.Body), BuildResponse(r)
}
func (c *Client) SearchBoardsForUser(teamID, term string, field model.BoardSearchField) ([]*model.Board, *Response) {
query := fmt.Sprintf("q=%s&field=%s", term, field)
r, err := c.DoAPIGet(c.GetTeamRoute(teamID)+"/boards/search?"+query, "")
if err != nil {
return nil, BuildErrorResponse(r, err)
}
defer closeBody(r)
return model.BoardsFromJSON(r.Body), BuildResponse(r)
}
func (c *Client) SearchBoardsForTeam(teamID, term string) ([]*model.Board, *Response) {
r, err := c.DoAPIGet(c.GetTeamRoute(teamID)+"/boards/search?q="+term, "")
if err != nil {
return nil, BuildErrorResponse(r, err)
}
defer closeBody(r)
return model.BoardsFromJSON(r.Body), BuildResponse(r)
}
func (c *Client) GetMembersForBoard(boardID string) ([]*model.BoardMember, *Response) {
r, err := c.DoAPIGet(c.GetBoardRoute(boardID)+"/members", "")
if err != nil {
return nil, BuildErrorResponse(r, err)
}
defer closeBody(r)
return model.BoardMembersFromJSON(r.Body), BuildResponse(r)
}
func (c *Client) AddMemberToBoard(member *model.BoardMember) (*model.BoardMember, *Response) {
r, err := c.DoAPIPost(c.GetBoardRoute(member.BoardID)+"/members", toJSON(member))
if err != nil {
return nil, BuildErrorResponse(r, err)
}
defer closeBody(r)
return model.BoardMemberFromJSON(r.Body), BuildResponse(r)
}
func (c *Client) JoinBoard(boardID string) (*model.BoardMember, *Response) {
r, err := c.DoAPIPost(c.GetJoinBoardRoute(boardID), "")
if err != nil {
return nil, BuildErrorResponse(r, err)
}
defer closeBody(r)
return model.BoardMemberFromJSON(r.Body), BuildResponse(r)
}
func (c *Client) LeaveBoard(boardID string) (*model.BoardMember, *Response) {
r, err := c.DoAPIPost(c.GetLeaveBoardRoute(boardID), "")
if err != nil {
return nil, BuildErrorResponse(r, err)
}
defer closeBody(r)
return model.BoardMemberFromJSON(r.Body), BuildResponse(r)
}
func (c *Client) UpdateBoardMember(member *model.BoardMember) (*model.BoardMember, *Response) {
r, err := c.DoAPIPut(c.GetBoardRoute(member.BoardID)+"/members/"+member.UserID, toJSON(member))
if err != nil {
return nil, BuildErrorResponse(r, err)
}
defer closeBody(r)
return model.BoardMemberFromJSON(r.Body), BuildResponse(r)
}
func (c *Client) DeleteBoardMember(member *model.BoardMember) (bool, *Response) {
r, err := c.DoAPIDelete(c.GetBoardRoute(member.BoardID)+"/members/"+member.UserID, "")
if err != nil {
return false, BuildErrorResponse(r, err)
}
defer closeBody(r)
return true, BuildResponse(r)
}
func (c *Client) GetTeamUploadFileRoute(teamID, boardID string) string {
return fmt.Sprintf("%s/%s/files", c.GetTeamRoute(teamID), boardID)
}
func (c *Client) TeamUploadFile(teamID, boardID string, data io.Reader) (*api.FileUploadResponse, *Response) {
body := &bytes.Buffer{}
writer := multipart.NewWriter(body)
part, err := writer.CreateFormFile(api.UploadFormFileKey, "file")
if err != nil {
return nil, &Response{Error: err}
}
if _, err = io.Copy(part, data); err != nil {
return nil, &Response{Error: err}
}
writer.Close()
opt := func(r *http.Request) {
r.Header.Add("Content-Type", writer.FormDataContentType())
}
r, err := c.doAPIRequestReader(http.MethodPost, c.APIURL+c.GetTeamUploadFileRoute(teamID, boardID), body, "", opt)
if err != nil {
return nil, BuildErrorResponse(r, err)
}
defer closeBody(r)
fileUploadResponse, err := api.FileUploadResponseFromJSON(r.Body)
if err != nil {
return nil, BuildErrorResponse(r, err)
}
return fileUploadResponse, BuildResponse(r)
}
func (c *Client) TeamUploadFileInfo(teamID, boardID string, fileName string) (*mmModel.FileInfo, *Response) {
r, err := c.DoAPIGet(fmt.Sprintf("/files/teams/%s/%s/%s/info", teamID, boardID, fileName), "")
if err != nil {
return nil, BuildErrorResponse(r, err)
}
defer closeBody(r)
fileInfoResponse, error := api.FileInfoResponseFromJSON(r.Body)
if error != nil {
return nil, BuildErrorResponse(r, error)
}
return fileInfoResponse, BuildResponse(r)
}
func (c *Client) GetSubscriptionsRoute() string {
return "/subscriptions"
}
func (c *Client) CreateSubscription(sub *model.Subscription) (*model.Subscription, *Response) {
r, err := c.DoAPIPost(c.GetSubscriptionsRoute(), toJSON(&sub))
if err != nil {
return nil, BuildErrorResponse(r, err)
}
defer closeBody(r)
subNew, err := model.SubscriptionFromJSON(r.Body)
if err != nil {
return nil, BuildErrorResponse(r, err)
}
return subNew, BuildResponse(r)
}
func (c *Client) DeleteSubscription(blockID string, subscriberID string) *Response {
url := fmt.Sprintf("%s/%s/%s", c.GetSubscriptionsRoute(), blockID, subscriberID)
r, err := c.DoAPIDelete(url, "")
if err != nil {
return BuildErrorResponse(r, err)
}
defer closeBody(r)
return BuildResponse(r)
}
func (c *Client) GetSubscriptions(subscriberID string) ([]*model.Subscription, *Response) {
url := fmt.Sprintf("%s/%s", c.GetSubscriptionsRoute(), subscriberID)
r, err := c.DoAPIGet(url, "")
if err != nil {
return nil, BuildErrorResponse(r, err)
}
defer closeBody(r)
var subs []*model.Subscription
err = json.NewDecoder(r.Body).Decode(&subs)
if err != nil {
return nil, BuildErrorResponse(r, err)
}
return subs, BuildResponse(r)
}
func (c *Client) GetTemplatesForTeam(teamID string) ([]*model.Board, *Response) {
r, err := c.DoAPIGet(c.GetTeamRoute(teamID)+"/templates", "")
if err != nil {
return nil, BuildErrorResponse(r, err)
}
defer closeBody(r)
return model.BoardsFromJSON(r.Body), BuildResponse(r)
}
func (c *Client) ExportBoardArchive(boardID string) ([]byte, *Response) {
r, err := c.DoAPIGet(c.GetBoardRoute(boardID)+"/archive/export", "")
if err != nil {
return nil, BuildErrorResponse(r, err)
}
defer closeBody(r)
buf, err := io.ReadAll(r.Body)
if err != nil {
return nil, BuildErrorResponse(r, err)
}
return buf, BuildResponse(r)
}
func (c *Client) ImportArchive(teamID string, data io.Reader) *Response {
body := &bytes.Buffer{}
writer := multipart.NewWriter(body)
part, err := writer.CreateFormFile(api.UploadFormFileKey, "file")
if err != nil {
return &Response{Error: err}
}
if _, err = io.Copy(part, data); err != nil {
return &Response{Error: err}
}
writer.Close()
opt := func(r *http.Request) {
r.Header.Add("Content-Type", writer.FormDataContentType())
}
r, err := c.doAPIRequestReader(http.MethodPost, c.APIURL+c.GetTeamRoute(teamID)+"/archive/import", body, "", opt)
if err != nil {
return BuildErrorResponse(r, err)
}
defer closeBody(r)
return BuildResponse(r)
}
func (c *Client) GetLimits() (*model.BoardsCloudLimits, *Response) {
r, err := c.DoAPIGet("/limits", "")
if err != nil {
return nil, BuildErrorResponse(r, err)
}
defer closeBody(r)
var limits *model.BoardsCloudLimits
err = json.NewDecoder(r.Body).Decode(&limits)
if err != nil {
return nil, BuildErrorResponse(r, err)
}
return limits, BuildResponse(r)
}
func (c *Client) MoveContentBlock(srcBlockID string, dstBlockID string, where string, userID string) (bool, *Response) {
r, err := c.DoAPIPost("/content-blocks/"+srcBlockID+"/moveto/"+where+"/"+dstBlockID, "")
if err != nil {
return false, BuildErrorResponse(r, err)
}
defer closeBody(r)
return true, BuildResponse(r)
}
func (c *Client) GetStatistics() (*model.BoardsStatistics, *Response) {
r, err := c.DoAPIGet("/statistics", "")
if err != nil {
return nil, BuildErrorResponse(r, err)
}
defer closeBody(r)
var stats *model.BoardsStatistics
err = json.NewDecoder(r.Body).Decode(&stats)
if err != nil {
return nil, BuildErrorResponse(r, err)
}
return stats, BuildResponse(r)
}
func (c *Client) GetBoardsForCompliance(teamID string, page, perPage int) (*model.BoardsComplianceResponse, *Response) {
query := fmt.Sprintf("?team_id=%s&page=%d&per_page=%d", teamID, page, perPage)
r, err := c.DoAPIGet("/admin/boards"+query, "")
if err != nil {
return nil, BuildErrorResponse(r, err)
}
defer closeBody(r)
var res *model.BoardsComplianceResponse
err = json.NewDecoder(r.Body).Decode(&res)
if err != nil {
return nil, BuildErrorResponse(r, err)
}
return res, BuildResponse(r)
}
func (c *Client) GetBoardsComplianceHistory(
modifiedSince int64, includeDeleted bool, teamID string, page, perPage int) (*model.BoardsComplianceHistoryResponse, *Response) {
query := fmt.Sprintf("?modified_since=%d&include_deleted=%t&team_id=%s&page=%d&per_page=%d",
modifiedSince, includeDeleted, teamID, page, perPage)
r, err := c.DoAPIGet("/admin/boards_history"+query, "")
if err != nil {
return nil, BuildErrorResponse(r, err)
}
defer closeBody(r)
var res *model.BoardsComplianceHistoryResponse
err = json.NewDecoder(r.Body).Decode(&res)
if err != nil {
return nil, BuildErrorResponse(r, err)
}
return res, BuildResponse(r)
}
func (c *Client) GetBlocksComplianceHistory(
modifiedSince int64, includeDeleted bool, teamID, boardID string, page, perPage int) (*model.BlocksComplianceHistoryResponse, *Response) {
query := fmt.Sprintf("?modified_since=%d&include_deleted=%t&team_id=%s&board_id=%s&page=%d&per_page=%d",
modifiedSince, includeDeleted, teamID, boardID, page, perPage)
r, err := c.DoAPIGet("/admin/blocks_history"+query, "")
if err != nil {
return nil, BuildErrorResponse(r, err)
}
defer closeBody(r)
var res *model.BlocksComplianceHistoryResponse
err = json.NewDecoder(r.Body).Decode(&res)
if err != nil {
return nil, BuildErrorResponse(r, err)
}
return res, BuildResponse(r)
}
func (c *Client) HideBoard(teamID, categoryID, boardID string) *Response {
r, err := c.DoAPIPut(c.GetTeamRoute(teamID)+"/categories/"+categoryID+"/boards/"+boardID+"/hide", "")
if err != nil {
return BuildErrorResponse(r, err)
}
defer closeBody(r)
return BuildResponse(r)
}
func (c *Client) UnhideBoard(teamID, categoryID, boardID string) *Response {
r, err := c.DoAPIPut(c.GetTeamRoute(teamID)+"/categories/"+categoryID+"/boards/"+boardID+"/unhide", "")
if err != nil {
return BuildErrorResponse(r, err)
}
defer closeBody(r)
return BuildResponse(r)
}