joinBoard API (#2660)
This commit is contained in:
parent
a9d4a7457c
commit
efb8caf88f
8 changed files with 489 additions and 241 deletions
|
@ -99,6 +99,7 @@ func (a *API) RegisterRoutes(r *mux.Router) {
|
|||
apiv1.HandleFunc("/boards/{boardID}/members", a.sessionRequired(a.handleAddMember)).Methods("POST")
|
||||
apiv1.HandleFunc("/boards/{boardID}/members/{userID}", a.sessionRequired(a.handleUpdateMember)).Methods("PUT")
|
||||
apiv1.HandleFunc("/boards/{boardID}/members/{userID}", a.sessionRequired(a.handleDeleteMember)).Methods("DELETE")
|
||||
apiv1.HandleFunc("/boards/{boardID}/join", a.sessionRequired(a.handleJoinBoard)).Methods("POST")
|
||||
|
||||
// Sharing APIs
|
||||
apiv1.HandleFunc("/boards/{boardID}/sharing", a.sessionRequired(a.handlePostSharing)).Methods("POST")
|
||||
|
@ -3214,6 +3215,98 @@ func (a *API) handleAddMember(w http.ResponseWriter, r *http.Request) {
|
|||
auditRec.Success()
|
||||
}
|
||||
|
||||
func (a *API) handleJoinBoard(w http.ResponseWriter, r *http.Request) {
|
||||
// swagger:operation POST /boards/{boardID}/join joinBoard
|
||||
//
|
||||
// Become a member of a board
|
||||
//
|
||||
// ---
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: boardID
|
||||
// in: path
|
||||
// description: Board ID
|
||||
// required: true
|
||||
// type: string
|
||||
// security:
|
||||
// - BearerAuth: []
|
||||
// responses:
|
||||
// '200':
|
||||
// description: success
|
||||
// schema:
|
||||
// $ref: '#/definitions/BoardMember'
|
||||
// '404':
|
||||
// description: board not found
|
||||
// '503':
|
||||
// description: access denied
|
||||
// default:
|
||||
// description: internal error
|
||||
// schema:
|
||||
// "$ref": "#/definitions/ErrorResponse"
|
||||
|
||||
userID := getUserID(r)
|
||||
if userID == "" {
|
||||
a.errorResponse(w, r.URL.Path, http.StatusBadRequest, "", nil)
|
||||
return
|
||||
}
|
||||
|
||||
boardID := mux.Vars(r)["boardID"]
|
||||
board, err := a.app.GetBoard(boardID)
|
||||
if err != nil {
|
||||
a.errorResponse(w, r.URL.Path, http.StatusInternalServerError, "", err)
|
||||
return
|
||||
}
|
||||
if board == nil {
|
||||
a.errorResponse(w, r.URL.Path, http.StatusNotFound, "", nil)
|
||||
return
|
||||
}
|
||||
if board.Type != model.BoardTypeOpen {
|
||||
a.errorResponse(w, r.URL.Path, http.StatusForbidden, "", nil)
|
||||
return
|
||||
}
|
||||
|
||||
if !a.permissions.HasPermissionToTeam(userID, board.TeamID, model.PermissionViewTeam) {
|
||||
a.errorResponse(w, r.URL.Path, http.StatusForbidden, "", nil)
|
||||
return
|
||||
}
|
||||
|
||||
// currently all memberships are created as editors by default
|
||||
// TODO: Support different public roles
|
||||
newBoardMember := &model.BoardMember{
|
||||
UserID: userID,
|
||||
BoardID: boardID,
|
||||
SchemeEditor: true,
|
||||
}
|
||||
|
||||
auditRec := a.makeAuditRecord(r, "joinBoard", audit.Fail)
|
||||
defer a.audit.LogRecord(audit.LevelModify, auditRec)
|
||||
auditRec.AddMeta("boardID", boardID)
|
||||
auditRec.AddMeta("addedUserID", userID)
|
||||
|
||||
member, err := a.app.AddMemberToBoard(newBoardMember)
|
||||
if err != nil {
|
||||
a.errorResponse(w, r.URL.Path, http.StatusInternalServerError, "", err)
|
||||
return
|
||||
}
|
||||
|
||||
a.logger.Debug("AddMember",
|
||||
mlog.String("boardID", board.ID),
|
||||
mlog.String("addedUserID", userID),
|
||||
)
|
||||
|
||||
data, err := json.Marshal(member)
|
||||
if err != nil {
|
||||
a.errorResponse(w, r.URL.Path, http.StatusInternalServerError, "", err)
|
||||
return
|
||||
}
|
||||
|
||||
// response
|
||||
jsonBytesResponse(w, http.StatusOK, data)
|
||||
|
||||
auditRec.Success()
|
||||
}
|
||||
|
||||
func (a *API) handleUpdateMember(w http.ResponseWriter, r *http.Request) {
|
||||
// swagger:operation PUT /boards/{boardID}/members/{userID} updateMember
|
||||
//
|
||||
|
|
|
@ -176,6 +176,10 @@ func (c *Client) GetBoardRoute(boardID string) string {
|
|||
return fmt.Sprintf("%s/%s", c.GetBoardsRoute(), boardID)
|
||||
}
|
||||
|
||||
func (c *Client) GetJoinBoardRoute(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))
|
||||
}
|
||||
|
@ -512,6 +516,16 @@ func (c *Client) AddMemberToBoard(member *model.BoardMember) (*model.BoardMember
|
|||
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) UpdateBoardMember(member *model.BoardMember) (*model.BoardMember, *Response) {
|
||||
r, err := c.DoAPIPut(c.GetBoardRoute(member.BoardID)+"/members/"+member.UserID, toJSON(member))
|
||||
if err != nil {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package integrationtests
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/mattermost/focalboard/server/client"
|
||||
|
@ -1268,3 +1269,78 @@ func TestDeleteMember(t *testing.T) {
|
|||
require.True(t, members[0].SchemeAdmin)
|
||||
})
|
||||
}
|
||||
|
||||
func TestJoinBoard(t *testing.T) {
|
||||
t.Run("create and join public board", func(t *testing.T) {
|
||||
th := SetupTestHelper(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
me := th.GetUser1()
|
||||
|
||||
title := "Public board"
|
||||
teamID := testTeamID
|
||||
newBoard := &model.Board{
|
||||
Title: title,
|
||||
Type: model.BoardTypeOpen,
|
||||
TeamID: teamID,
|
||||
}
|
||||
board, resp := th.Client.CreateBoard(newBoard)
|
||||
th.CheckOK(resp)
|
||||
require.NoError(t, resp.Error)
|
||||
require.NotNil(t, board)
|
||||
require.NotNil(t, board.ID)
|
||||
require.Equal(t, title, board.Title)
|
||||
require.Equal(t, model.BoardTypeOpen, board.Type)
|
||||
require.Equal(t, teamID, board.TeamID)
|
||||
require.Equal(t, me.ID, board.CreatedBy)
|
||||
require.Equal(t, me.ID, board.ModifiedBy)
|
||||
|
||||
member, resp := th.Client2.JoinBoard(board.ID)
|
||||
th.CheckOK(resp)
|
||||
require.NoError(t, resp.Error)
|
||||
require.NotNil(t, member)
|
||||
require.Equal(t, board.ID, member.BoardID)
|
||||
require.Equal(t, th.GetUser2().ID, member.UserID)
|
||||
|
||||
s, _ := json.MarshalIndent(member, "", "\t")
|
||||
t.Log(string(s))
|
||||
})
|
||||
|
||||
t.Run("create and join private board (should not succeed)", func(t *testing.T) {
|
||||
th := SetupTestHelper(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
me := th.GetUser1()
|
||||
|
||||
title := "Private board"
|
||||
teamID := testTeamID
|
||||
newBoard := &model.Board{
|
||||
Title: title,
|
||||
Type: model.BoardTypePrivate,
|
||||
TeamID: teamID,
|
||||
}
|
||||
board, resp := th.Client.CreateBoard(newBoard)
|
||||
th.CheckOK(resp)
|
||||
require.NoError(t, resp.Error)
|
||||
require.NotNil(t, board)
|
||||
require.NotNil(t, board.ID)
|
||||
require.Equal(t, title, board.Title)
|
||||
require.Equal(t, model.BoardTypePrivate, board.Type)
|
||||
require.Equal(t, teamID, board.TeamID)
|
||||
require.Equal(t, me.ID, board.CreatedBy)
|
||||
require.Equal(t, me.ID, board.ModifiedBy)
|
||||
|
||||
member, resp := th.Client2.JoinBoard(board.ID)
|
||||
th.CheckForbidden(resp)
|
||||
require.Nil(t, member)
|
||||
})
|
||||
|
||||
t.Run("join invalid board", func(t *testing.T) {
|
||||
th := SetupTestHelper(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
member, resp := th.Client2.JoinBoard("nonexistent-board-ID")
|
||||
th.CheckNotFound(resp)
|
||||
require.Nil(t, member)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
5.3.0
|
||||
5.4.0
|
|
@ -1840,6 +1840,25 @@ paths:
|
|||
$ref: '#/definitions/ErrorResponse'
|
||||
security:
|
||||
- BearerAuth: []
|
||||
/api/v1/users/me/memberships:
|
||||
get:
|
||||
description: Returns the currently users board memberships
|
||||
operationId: getMyMemberships
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: success
|
||||
schema:
|
||||
items:
|
||||
$ref: '#/definitions/BoardMember'
|
||||
type: array
|
||||
default:
|
||||
description: internal error
|
||||
schema:
|
||||
$ref: '#/definitions/ErrorResponse'
|
||||
security:
|
||||
- BearerAuth: []
|
||||
/api/v1/workspaces/{workspaceID}/blocks/{blockID}/undelete:
|
||||
post:
|
||||
description: Undeletes a block
|
||||
|
@ -1900,6 +1919,33 @@ paths:
|
|||
$ref: '#/definitions/ErrorResponse'
|
||||
security:
|
||||
- BearerAuth: []
|
||||
/boards/{boardID}/join:
|
||||
post:
|
||||
description: Become a member of a board
|
||||
operationId: joinBoard
|
||||
parameters:
|
||||
- description: Board ID
|
||||
in: path
|
||||
name: boardID
|
||||
required: true
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: success
|
||||
schema:
|
||||
$ref: '#/definitions/BoardMember'
|
||||
"404":
|
||||
description: board not found
|
||||
"503":
|
||||
description: access denied
|
||||
default:
|
||||
description: internal error
|
||||
schema:
|
||||
$ref: '#/definitions/ErrorResponse'
|
||||
security:
|
||||
- BearerAuth: []
|
||||
/boards/{boardID}/members:
|
||||
post:
|
||||
description: Adds a new member to a board
|
||||
|
|
482
webapp/package-lock.json
generated
482
webapp/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -435,6 +435,21 @@ class OctoClient {
|
|||
return this.getJson<BoardMember>(response, {} as BoardMember)
|
||||
}
|
||||
|
||||
async joinBoard(boardId: string): Promise<BoardMember|undefined> {
|
||||
Utils.log(`joinBoard: board ${boardId}`)
|
||||
|
||||
const response = await fetch(this.getBaseURL() + `/api/v1/boards/${boardId}/join`, {
|
||||
method: 'POST',
|
||||
headers: this.headers()
|
||||
})
|
||||
|
||||
if (response.status !== 200) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
return this.getJson<BoardMember>(response, {} as BoardMember)
|
||||
}
|
||||
|
||||
async updateBoardMember(member: BoardMember): Promise<Response> {
|
||||
Utils.log(`udpateBoardMember: user ${member.userId} and board ${member.boardId}`)
|
||||
|
||||
|
|
|
@ -84,7 +84,7 @@ const BoardPage = (props: Props): JSX.Element => {
|
|||
// and fetch its data
|
||||
const result: any = await dispatch(loadBoardData(boardId))
|
||||
if (result.payload.blocks.length === 0 && userId) {
|
||||
const member = await octoClient.createBoardMember({userId, boardId})
|
||||
const member = await octoClient.joinBoard(boardId)
|
||||
if (!member) {
|
||||
UserSettings.setLastBoardID(boardTeamId, null)
|
||||
UserSettings.setLastViewId(boardId, null)
|
||||
|
|
Loading…
Reference in a new issue