Added server changes to support private templates

This commit is contained in:
Harshil Sharma 2022-03-23 16:21:48 +05:30
parent 22a92068b5
commit d1b7e601c5
11 changed files with 130 additions and 17 deletions

View file

@ -76,6 +76,7 @@ func (a *API) RegisterRoutes(r *mux.Router) {
apiv1.HandleFunc("/teams/{teamID}/boards", a.sessionRequired(a.handleGetBoards)).Methods("GET")
apiv1.HandleFunc("/teams/{teamID}/boards/search", a.sessionRequired(a.handleSearchBoards)).Methods("GET")
apiv1.HandleFunc("/teams/{teamID}/templates", a.sessionRequired(a.handleGetTemplates)).Methods("GET")
apiv1.HandleFunc("/templates", a.sessionRequired(a.handleGetDefaultTemplates)).Methods("GET")
apiv1.HandleFunc("/boards", a.sessionRequired(a.handleCreateBoard)).Methods("POST")
apiv1.HandleFunc("/boards/{boardID}", a.attachSession(a.handleGetBoard, false)).Methods("GET")
apiv1.HandleFunc("/boards/{boardID}", a.sessionRequired(a.handlePatchBoard)).Methods("PATCH")
@ -2184,7 +2185,7 @@ func (a *API) handleGetTemplates(w http.ResponseWriter, r *http.Request) {
auditRec.AddMeta("teamID", teamID)
// retrieve boards list
boards, err := a.app.GetTemplateBoards(teamID)
boards, err := a.app.GetTemplateBoards(teamID, userID)
if err != nil {
a.errorResponse(w, r.URL.Path, http.StatusInternalServerError, "", err)
return
@ -2208,6 +2209,62 @@ func (a *API) handleGetTemplates(w http.ResponseWriter, r *http.Request) {
auditRec.Success()
}
func (a *API) handleGetDefaultTemplates(w http.ResponseWriter, r *http.Request) {
// swagger:operation GET /api/v1/templates getDefaultTemplates
//
// Returns default templates
//
// ---
// produces:
// - application/json
// parameters:
// - name: teamID
// in: path
// description: Team ID
// required: true
// type: string
// security:
// - BearerAuth: []
// responses:
// '200':
// description: success
// schema:
// type: array
// items:
// items:
// "$ref": "#/definitions/Board"
// default:
// description: internal error
// schema:
// "$ref": "#/definitions/ErrorResponse"
auditRec := a.makeAuditRecord(r, "getDefaultTemplates", audit.Fail)
defer a.audit.LogRecord(audit.LevelRead, auditRec)
// retrieve boards list
boards, err := a.app.GetDefaultTemplates()
if err != nil {
a.errorResponse(w, r.URL.Path, http.StatusInternalServerError, "", err)
return
}
a.logger.Debug("GetDefaultTemplates",
mlog.Int("boardsCount", len(boards)),
)
data, err := json.Marshal(boards)
if err != nil {
a.errorResponse(w, r.URL.Path, http.StatusInternalServerError, "", err)
return
}
// response
jsonBytesResponse(w, http.StatusOK, data)
auditRec.AddMeta("templatesCount", len(boards))
auditRec.Success()
}
// subscriptions
func (a *API) handleCreateSubscription(w http.ResponseWriter, r *http.Request) {

View file

@ -49,8 +49,12 @@ func (a *App) GetBoardsForUserAndTeam(userID, teamID string) ([]*model.Board, er
return a.store.GetBoardsForUserAndTeam(userID, teamID)
}
func (a *App) GetTemplateBoards(teamID string) ([]*model.Board, error) {
return a.store.GetTemplateBoards(teamID)
func (a *App) GetTemplateBoards(teamID, userID string) ([]*model.Board, error) {
return a.store.GetTemplateBoards(teamID, userID)
}
func (a *App) GetDefaultTemplates() ([]*model.Board, error) {
return a.store.GetDefaultTemplates()
}
func (a *App) CreateBoard(board *model.Board, userID string, addMember bool) (*model.Board, error) {

View file

@ -46,7 +46,7 @@ func (a *App) PrepareOnboardingTour(userID string, teamID string) (string, strin
}
func (a *App) getOnboardingBoardID() (string, error) {
boards, err := a.store.GetTemplateBoards(globalTeamID)
boards, err := a.store.GetDefaultTemplates()
if err != nil {
return "", err
}

View file

@ -26,7 +26,7 @@ func (a *App) InitTemplates() error {
// initializeTemplates imports default templates if the boards table is empty.
func (a *App) initializeTemplates() error {
boards, err := a.store.GetTemplateBoards(globalTeamID)
boards, err := a.store.GetDefaultTemplates()
if err != nil {
return fmt.Errorf("cannot initialize templates: %w", err)
}

View file

@ -535,6 +535,21 @@ func (mr *MockStoreMockRecorder) GetCategory(arg0 interface{}) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCategory", reflect.TypeOf((*MockStore)(nil).GetCategory), arg0)
}
// GetDefaultTemplates mocks base method.
func (m *MockStore) GetDefaultTemplates() ([]*model.Board, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetDefaultTemplates")
ret0, _ := ret[0].([]*model.Board)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetDefaultTemplates indicates an expected call of GetDefaultTemplates.
func (mr *MockStoreMockRecorder) GetDefaultTemplates() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDefaultTemplates", reflect.TypeOf((*MockStore)(nil).GetDefaultTemplates))
}
// GetMemberForBoard mocks base method.
func (m *MockStore) GetMemberForBoard(arg0, arg1 string) (*model.BoardMember, error) {
m.ctrl.T.Helper()
@ -821,18 +836,18 @@ func (mr *MockStoreMockRecorder) GetTeamsForUser(arg0 interface{}) *gomock.Call
}
// GetTemplateBoards mocks base method.
func (m *MockStore) GetTemplateBoards(arg0 string) ([]*model.Board, error) {
func (m *MockStore) GetTemplateBoards(arg0, arg1 string) ([]*model.Board, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetTemplateBoards", arg0)
ret := m.ctrl.Call(m, "GetTemplateBoards", arg0, arg1)
ret0, _ := ret[0].([]*model.Board)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetTemplateBoards indicates an expected call of GetTemplateBoards.
func (mr *MockStoreMockRecorder) GetTemplateBoards(arg0 interface{}) *gomock.Call {
func (mr *MockStoreMockRecorder) GetTemplateBoards(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTemplateBoards", reflect.TypeOf((*MockStore)(nil).GetTemplateBoards), arg0)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTemplateBoards", reflect.TypeOf((*MockStore)(nil).GetTemplateBoards), arg0, arg1)
}
// GetUserByEmail mocks base method.

View file

@ -367,12 +367,10 @@ INSERT INTO {{.prefix}}board_members (
SELECT B.Id, CM.UserId, CM.Roles, (CM.UserId=B.created_by) OR CM.SchemeAdmin, CM.SchemeUser, FALSE, CM.SchemeGuest
FROM {{.prefix}}boards AS B
INNER JOIN ChannelMembers as CM ON CM.ChannelId=B.channel_id
WHERE NOT B.is_template
);
{{else}}
{{- /* if we're in personal server or desktop, create memberships for everyone */ -}}
INSERT INTO {{.prefix}}board_members
SELECT B.id, U.id, '', B.created_by=U.id, TRUE, FALSE, FALSE
FROM {{.prefix}}boards AS B, {{.prefix}}users AS U
WHERE NOT B.is_template;
{{end}}

View file

@ -319,6 +319,11 @@ func (s *SQLStore) GetCategory(id string) (*model.Category, error) {
}
func (s *SQLStore) GetDefaultTemplates() ([]*model.Board, error) {
return s.getDefaultTemplates(s.db)
}
func (s *SQLStore) GetMemberForBoard(boardID string, userID string) (*model.BoardMember, error) {
return s.getMemberForBoard(s.db, boardID, userID)
@ -414,8 +419,8 @@ func (s *SQLStore) GetTeamsForUser(userID string) ([]*model.Team, error) {
}
func (s *SQLStore) GetTemplateBoards(teamID string) ([]*model.Board, error) {
return s.getTemplateBoards(s.db, teamID)
func (s *SQLStore) GetTemplateBoards(teamID string, userID string) ([]*model.Board, error) {
return s.getTemplateBoards(s.db, teamID, userID)
}

View file

@ -55,11 +55,11 @@ func (s *SQLStore) removeDefaultTemplates(db sq.BaseRunner, boards []*model.Boar
}
// getDefaultTemplateBoards fetches all template blocks .
func (s *SQLStore) getTemplateBoards(db sq.BaseRunner, teamID string) ([]*model.Board, error) {
func (s *SQLStore) getDefaultTemplates(db sq.BaseRunner) ([]*model.Board, error) {
query := s.getQueryBuilder(db).
Select(boardFields("")...).
From(s.tablePrefix + "boards").
Where(sq.Eq{"coalesce(team_id, '0')": teamID}).
Where(sq.Eq{"team_id": "0"}).
Where(sq.Eq{"is_template": true})
rows, err := query.Query()
@ -71,3 +71,27 @@ func (s *SQLStore) getTemplateBoards(db sq.BaseRunner, teamID string) ([]*model.
return s.boardsFromRows(rows)
}
// getDefaultTemplateBoards fetches all template blocks .
func (s *SQLStore) getTemplateBoards(db sq.BaseRunner, teamID, userID string) ([]*model.Board, error) {
query := s.getQueryBuilder(db).
Select(boardFields("")...).
From(s.tablePrefix+"boards as b").
Join(s.tablePrefix+"board_members as bm on b.id = bm.board_id and b.team_id = ? and bm.user_id = ?", teamID, userID).
Where(sq.Eq{"is_template": true})
rows, err := query.Query()
if err != nil {
s.logger.Error(`getTemplateBoards ERROR`, mlog.Err(err))
return nil, err
}
defer s.CloseRows(rows)
userTemplates, err := s.boardsFromRows(rows)
if err != nil {
return nil, err
}
return userTemplates, nil
}

View file

@ -124,8 +124,9 @@ type Store interface {
GetNotificationHint(blockID string) (*model.NotificationHint, error)
GetNextNotificationHint(remove bool) (*model.NotificationHint, error)
GetDefaultTemplates() ([]*model.Board, error)
RemoveDefaultTemplates(boards []*model.Board) error
GetTemplateBoards(teamID string) ([]*model.Board, error)
GetTemplateBoards(teamID, userID string) ([]*model.Board, error)
DBType() string

View file

@ -596,6 +596,15 @@ class OctoClient {
return this.getBoardsWithPath(path)
}
async getDefaultTemplates(): Promise<Board[]> {
const path = '/api/v1/templates'
const response = await fetch(this.getBaseURL() + path, {headers: this.headers()})
if (response.status !== 200) {
return []
}
return (await this.getJson(response, [])) as Board[]
}
// Boards
// ToDo: .
// - goal? make the interface show boards & blocks for boards

View file

@ -13,7 +13,7 @@ import {RootState} from './index'
export const fetchGlobalTemplates = createAsyncThunk(
'globalTemplates/fetch',
async () => {
const templates = await client.getTeamTemplates('0')
const templates = await client.getDefaultTemplates()
return templates.sort((a, b) => a.title.localeCompare(b.title))
},
)