mirror of
https://github.com/mattermost/focalboard.git
synced 2025-01-11 18:13:52 +02:00
Fetching board member in parellel (#3379)
* Translated using Weblate (Malayalam) Currently translated at 100.0% (303 of 303 strings) Translation: Focalboard/webapp Translate-URL: https://translate.mattermost.com/projects/focalboard/webapp/ml/ * Update translation files Updated by "Cleanup translation files" hook in Weblate. Translation: Focalboard/webapp Translate-URL: https://translate.mattermost.com/projects/focalboard/webapp/ Update translation files Updated by "Cleanup translation files" hook in Weblate. Translation: Focalboard/webapp Translate-URL: https://translate.mattermost.com/projects/focalboard/webapp/ Update translation files Updated by "Cleanup translation files" hook in Weblate. Translation: Focalboard/webapp Translate-URL: https://translate.mattermost.com/projects/focalboard/webapp/ * Translated using Weblate (German) Currently translated at 100.0% (312 of 312 strings) Translation: Focalboard/webapp Translate-URL: https://translate.mattermost.com/projects/focalboard/webapp/de/ * Added code for fetching all the users at one time. * Added the code for personal server users * Update-code * Linter fixes * Test cases updated, some minor changes * Update the code according to review comments * Cypress test fix * Update server/services/store/mattermostauthlayer/mattermostauthlayer.go Co-authored-by: Varghese Jose <varghese.jose@tutanota.com> Co-authored-by: Hosted Weblate <hosted@weblate.org> Co-authored-by: jprusch <rs@schaeferbarthold.de> Co-authored-by: Scott Bishel <scott.bishel@mattermost.com>
This commit is contained in:
parent
d8ffd30fff
commit
ee395d49b1
@ -134,6 +134,7 @@ func (a *API) RegisterRoutes(r *mux.Router) {
|
||||
apiv2.HandleFunc("/teams/{teamID}/{boardID}/files", a.sessionRequired(a.handleUploadFile)).Methods("POST")
|
||||
|
||||
// User APIs
|
||||
apiv2.HandleFunc("/users", a.sessionRequired(a.handleGetUsersList)).Methods("POST")
|
||||
apiv2.HandleFunc("/users/me", a.sessionRequired(a.handleGetMe)).Methods("GET")
|
||||
apiv2.HandleFunc("/users/me/memberships", a.sessionRequired(a.handleGetMyMemberships)).Methods("GET")
|
||||
apiv2.HandleFunc("/users/{userID}", a.sessionRequired(a.handleGetUser)).Methods("GET")
|
||||
@ -1063,6 +1064,63 @@ func (a *API) handleGetUser(w http.ResponseWriter, r *http.Request) {
|
||||
auditRec.Success()
|
||||
}
|
||||
|
||||
func (a *API) handleGetUsersList(w http.ResponseWriter, r *http.Request) {
|
||||
// swagger:operation POST /users getUser
|
||||
//
|
||||
// Returns a user[]
|
||||
//
|
||||
// ---
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: userID
|
||||
// in: path
|
||||
// description: User ID
|
||||
// required: true
|
||||
// type: string
|
||||
// security:
|
||||
// - BearerAuth: []
|
||||
// responses:
|
||||
// '200':
|
||||
// description: success
|
||||
// schema:
|
||||
// "$ref": "#/definitions/User"
|
||||
// default:
|
||||
// description: internal error
|
||||
// schema:
|
||||
// "$ref": "#/definitions/ErrorResponse"
|
||||
|
||||
requestBody, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
a.errorResponse(w, r.URL.Path, http.StatusInternalServerError, "", err)
|
||||
return
|
||||
}
|
||||
|
||||
var userIDs []string
|
||||
if err = json.Unmarshal(requestBody, &userIDs); err != nil {
|
||||
a.errorResponse(w, r.URL.Path, http.StatusInternalServerError, "", err)
|
||||
return
|
||||
}
|
||||
|
||||
auditRec := a.makeAuditRecord(r, "getUsersList", audit.Fail)
|
||||
defer a.audit.LogRecord(audit.LevelAuth, auditRec)
|
||||
|
||||
users, err := a.app.GetUsersList(userIDs)
|
||||
if err != nil {
|
||||
a.errorResponse(w, r.URL.Path, http.StatusBadRequest, err.Error(), err)
|
||||
return
|
||||
}
|
||||
|
||||
usersList, err := json.Marshal(users)
|
||||
if err != nil {
|
||||
a.errorResponse(w, r.URL.Path, http.StatusBadRequest, "", err)
|
||||
return
|
||||
}
|
||||
|
||||
jsonStringResponse(w, http.StatusOK, string(usersList))
|
||||
auditRec.Success()
|
||||
}
|
||||
|
||||
func (a *API) handleGetMe(w http.ResponseWriter, r *http.Request) {
|
||||
// swagger:operation GET /users/me getMe
|
||||
//
|
||||
|
@ -64,6 +64,18 @@ func (a *App) GetUser(id string) (*model.User, error) {
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (a *App) GetUsersList(userIDs []string) ([]*model.User, error) {
|
||||
if len(userIDs) == 0 {
|
||||
return nil, errors.New("No User IDs")
|
||||
}
|
||||
|
||||
users, err := a.store.GetUsersList(userIDs)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "unable to find users")
|
||||
}
|
||||
return users, nil
|
||||
}
|
||||
|
||||
// Login create a new user session if the authentication data is valid.
|
||||
func (a *App) Login(username, email, password, mfaToken string) (string, error) {
|
||||
var user *model.User
|
||||
|
@ -457,6 +457,21 @@ func (mr *MockAPIMockRecorder) EnablePlugin(arg0 interface{}) *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnablePlugin", reflect.TypeOf((*MockAPI)(nil).EnablePlugin), arg0)
|
||||
}
|
||||
|
||||
// EnsureBotUser mocks base method.
|
||||
func (m *MockAPI) EnsureBotUser(arg0 *model.Bot) (string, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "EnsureBotUser", arg0)
|
||||
ret0, _ := ret[0].(string)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// EnsureBotUser indicates an expected call of EnsureBotUser.
|
||||
func (mr *MockAPIMockRecorder) EnsureBotUser(arg0 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnsureBotUser", reflect.TypeOf((*MockAPI)(nil).EnsureBotUser), arg0)
|
||||
}
|
||||
|
||||
// ExecuteSlashCommand mocks base method.
|
||||
func (m *MockAPI) ExecuteSlashCommand(arg0 *model.CommandArgs) (*model.CommandResponse, error) {
|
||||
m.ctrl.T.Helper()
|
||||
|
@ -295,6 +295,28 @@ func (s *MattermostAuthLayer) GetUsersByTeam(teamID string) ([]*model.User, erro
|
||||
return users, nil
|
||||
}
|
||||
|
||||
func (s *MattermostAuthLayer) GetUsersList(userIDs []string) ([]*model.User, error) {
|
||||
query := s.getQueryBuilder().
|
||||
Select("u.id", "u.username", "u.email", "u.nickname", "u.firstname", "u.lastname", "u.props", "u.CreateAt as create_at", "u.UpdateAt as update_at",
|
||||
"u.DeleteAt as delete_at", "b.UserId IS NOT NULL AS is_bot").
|
||||
From("Users as u").
|
||||
LeftJoin("Bots b ON ( b.UserId = Users.ID )").
|
||||
Where(sq.Eq{"u.id": userIDs})
|
||||
|
||||
rows, err := query.Query()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer s.CloseRows(rows)
|
||||
|
||||
users, err := s.usersFromRows(rows)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return users, nil
|
||||
}
|
||||
|
||||
func (s *MattermostAuthLayer) SearchUsersByTeam(teamID string, searchQuery string) ([]*model.User, error) {
|
||||
query := s.getQueryBuilder().
|
||||
Select("u.id", "u.username", "u.email", "u.nickname", "u.firstname", "u.lastname", "u.props", "u.CreateAt as create_at", "u.UpdateAt as update_at",
|
||||
|
@ -1060,6 +1060,21 @@ func (mr *MockStoreMockRecorder) GetUsersByTeam(arg0 interface{}) *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUsersByTeam", reflect.TypeOf((*MockStore)(nil).GetUsersByTeam), arg0)
|
||||
}
|
||||
|
||||
// GetUsersList mocks base method.
|
||||
func (m *MockStore) GetUsersList(arg0 []string) ([]*model.User, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetUsersList", arg0)
|
||||
ret0, _ := ret[0].([]*model.User)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetUsersList indicates an expected call of GetUsersList.
|
||||
func (mr *MockStoreMockRecorder) GetUsersList(arg0 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUsersList", reflect.TypeOf((*MockStore)(nil).GetUsersList), arg0)
|
||||
}
|
||||
|
||||
// InsertBlock mocks base method.
|
||||
func (m *MockStore) InsertBlock(arg0 *model.Block, arg1 string) error {
|
||||
m.ctrl.T.Helper()
|
||||
|
@ -514,6 +514,11 @@ func (s *SQLStore) GetUsersByTeam(teamID string) ([]*model.User, error) {
|
||||
|
||||
}
|
||||
|
||||
func (s *SQLStore) GetUsersList(userIDs []string) ([]*model.User, error) {
|
||||
return s.getUsersList(s.db, userIDs)
|
||||
|
||||
}
|
||||
|
||||
func (s *SQLStore) InsertBlock(block *model.Block, userID string) error {
|
||||
if s.dbType == model.SqliteDBType {
|
||||
return s.insertBlock(s.db, block, userID)
|
||||
|
@ -101,6 +101,10 @@ func (s *SQLStore) getUserByID(db sq.BaseRunner, userID string) (*model.User, er
|
||||
return s.getUserByCondition(db, sq.Eq{"id": userID})
|
||||
}
|
||||
|
||||
func (s *SQLStore) getUsersList(db sq.BaseRunner, userIDs []string) ([]*model.User, error) {
|
||||
return s.getUsersByCondition(db, sq.Eq{"id": userIDs}, 0)
|
||||
}
|
||||
|
||||
func (s *SQLStore) getUserByEmail(db sq.BaseRunner, email string) (*model.User, error) {
|
||||
return s.getUserByCondition(db, sq.Eq{"email": email})
|
||||
}
|
||||
|
@ -55,6 +55,7 @@ type Store interface {
|
||||
|
||||
GetRegisteredUserCount() (int, error)
|
||||
GetUserByID(userID string) (*model.User, error)
|
||||
GetUsersList(userIDs []string) ([]*model.User, error)
|
||||
GetUserByEmail(email string) (*model.User, error)
|
||||
GetUserByUsername(username string) (*model.User, error)
|
||||
CreateUser(user *model.User) error
|
||||
|
@ -457,6 +457,21 @@ func (mr *MockAPIMockRecorder) EnablePlugin(arg0 interface{}) *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnablePlugin", reflect.TypeOf((*MockAPI)(nil).EnablePlugin), arg0)
|
||||
}
|
||||
|
||||
// EnsureBotUser mocks base method.
|
||||
func (m *MockAPI) EnsureBotUser(arg0 *model.Bot) (string, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "EnsureBotUser", arg0)
|
||||
ret0, _ := ret[0].(string)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// EnsureBotUser indicates an expected call of EnsureBotUser.
|
||||
func (mr *MockAPIMockRecorder) EnsureBotUser(arg0 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnsureBotUser", reflect.TypeOf((*MockAPI)(nil).EnsureBotUser), arg0)
|
||||
}
|
||||
|
||||
// ExecuteSlashCommand mocks base method.
|
||||
func (m *MockAPI) ExecuteSlashCommand(arg0 *model.CommandArgs) (*model.CommandResponse, error) {
|
||||
m.ctrl.T.Helper()
|
||||
|
@ -99,7 +99,7 @@ describe('Card URL Property', () => {
|
||||
cy.log(`**Add ${type} view**`)
|
||||
// Intercept and wait for getUser request because it is the last one in the effects for BoardPage
|
||||
// After this last request the BoardPage component will not have additional rerenders
|
||||
cy.intercept('GET', '/api/v2/users/u*').as('getUser')
|
||||
cy.intercept('POST', '/api/v2/users').as('getUser')
|
||||
cy.findByRole('button', {name: 'View menu'}).click()
|
||||
cy.findByText('Add view').realHover()
|
||||
cy.findByRole('button', {name: type}).click()
|
||||
|
@ -187,6 +187,23 @@ class OctoClient {
|
||||
return user
|
||||
}
|
||||
|
||||
async getUsersList(userIds: string[]): Promise<IUser[] | []> {
|
||||
const path = `/api/v2/users`
|
||||
const body = JSON.stringify(userIds)
|
||||
const response = await fetch(this.getBaseURL() + path, {
|
||||
headers: this.headers(),
|
||||
method: 'POST',
|
||||
body,
|
||||
})
|
||||
|
||||
if(response.status !== 200) {
|
||||
return []
|
||||
}
|
||||
|
||||
return (await this.getJson(response, [])) as IUser[]
|
||||
|
||||
}
|
||||
|
||||
async patchUserConfig(userID: string, patch: UserConfigPatch): Promise<Record<string, string> | undefined> {
|
||||
const path = `/api/v2/users/${encodeURIComponent(userID)}/config`
|
||||
const body = JSON.stringify(patch)
|
||||
|
@ -28,16 +28,10 @@ export const fetchBoardMembers = createAsyncThunk(
|
||||
async ({teamId, boardId}: {teamId: string, boardId: string}, thunkAPI: any) => {
|
||||
const members = await client.getBoardMembers(teamId, boardId)
|
||||
const users = [] as IUser[]
|
||||
const userIDs = members.map((member) => member.userId)
|
||||
|
||||
/* eslint-disable no-await-in-loop */
|
||||
for (const member of members) {
|
||||
// TODO #2968 we should fetch this in bulk
|
||||
const user = await client.getUser(member.userId)
|
||||
if (user) {
|
||||
users.push(user)
|
||||
}
|
||||
}
|
||||
/* eslint-enable no-await-in-loop */
|
||||
const usersData = await client.getUsersList(userIDs)
|
||||
users.push(...usersData)
|
||||
|
||||
thunkAPI.dispatch(setBoardUsers(users))
|
||||
return members
|
||||
|
Loading…
Reference in New Issue
Block a user