From e96db725eafa689dde763d0fee33a9c83d33cc33 Mon Sep 17 00:00:00 2001 From: Julien Tant <785518+JulienTant@users.noreply.github.com> Date: Wed, 15 May 2024 07:06:40 -0700 Subject: [PATCH] PluginAPI: add ability to retrieve users by ids (#26936) * pluginapi: ability to retrieve users by ids * fix test --- server/channels/app/plugin_api.go | 4 ++ server/channels/app/plugin_api_test.go | 55 +++++++++++++++++++ server/public/plugin/api.go | 6 ++ .../plugin/api_timer_layer_generated.go | 7 +++ server/public/plugin/client_rpc_generated.go | 29 ++++++++++ server/public/plugin/plugintest/api.go | 32 +++++++++++ server/public/pluginapi/user.go | 9 +++ server/public/pluginapi/user_test.go | 29 ++++++++++ 8 files changed, 171 insertions(+) diff --git a/server/channels/app/plugin_api.go b/server/channels/app/plugin_api.go index 4b1cbd0a99..ff9d929f57 100644 --- a/server/channels/app/plugin_api.go +++ b/server/channels/app/plugin_api.go @@ -246,6 +246,10 @@ func (api *PluginAPI) GetUsers(options *model.UserGetOptions) ([]*model.User, *m return api.app.GetUsersFromProfiles(options) } +func (api *PluginAPI) GetUsersByIds(usersID []string) ([]*model.User, *model.AppError) { + return api.app.GetUsers(usersID) +} + func (api *PluginAPI) GetUser(userID string) (*model.User, *model.AppError) { return api.app.GetUser(userID) } diff --git a/server/channels/app/plugin_api_test.go b/server/channels/app/plugin_api_test.go index 5748416e3e..ba168a2b1e 100644 --- a/server/channels/app/plugin_api_test.go +++ b/server/channels/app/plugin_api_test.go @@ -431,6 +431,61 @@ func TestPluginAPIGetUsers(t *testing.T) { } } +func TestPluginAPIGetUsersByIds(t *testing.T) { + th := Setup(t).DeleteBots() + defer th.TearDown() + api := th.SetupPluginAPI() + + user1, err := th.App.CreateUser(th.Context, &model.User{ + Email: strings.ToLower(model.NewId()) + "success+test@example.com", + Password: "password", + Username: "user1" + model.NewId(), + }) + require.Nil(t, err) + defer th.App.PermanentDeleteUser(th.Context, user1) + + user2, err := th.App.CreateUser(th.Context, &model.User{ + Email: strings.ToLower(model.NewId()) + "success+test@example.com", + Password: "password", + Username: "user2" + model.NewId(), + }) + require.Nil(t, err) + defer th.App.PermanentDeleteUser(th.Context, user2) + + user3, err := th.App.CreateUser(th.Context, &model.User{ + Email: strings.ToLower(model.NewId()) + "success+test@example.com", + Password: "password", + Username: "user3" + model.NewId(), + }) + require.Nil(t, err) + defer th.App.PermanentDeleteUser(th.Context, user3) + + testCases := []struct { + Description string + requestedIDs []string + }{ + { + "no users", + []string{}, + }, + { + "getting 1 and 3", + []string{user1.Id, user3.Id}, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.Description, func(t *testing.T) { + users, err := api.GetUsersByIds(testCase.requestedIDs) + assert.Nil(t, err) + assert.Equal(t, len(testCase.requestedIDs), len(users)) + for _, user := range users { + assert.Contains(t, testCase.requestedIDs, user.Id) + } + }) + } +} + func TestPluginAPIGetUsersInTeam(t *testing.T) { th := Setup(t) defer th.TearDown() diff --git a/server/public/plugin/api.go b/server/public/plugin/api.go index 7631d529f9..bc540024d8 100644 --- a/server/public/plugin/api.go +++ b/server/public/plugin/api.go @@ -135,6 +135,12 @@ type API interface { // Minimum server version: 5.10 GetUsers(options *model.UserGetOptions) ([]*model.User, *model.AppError) + // GetUsersByIds gets a list of users by their IDs. + // + // @tag User + // Minimum server version: 9.8 + GetUsersByIds(userIDs []string) ([]*model.User, *model.AppError) + // GetUser gets a user. // // @tag User diff --git a/server/public/plugin/api_timer_layer_generated.go b/server/public/plugin/api_timer_layer_generated.go index fb569fc697..2643adb8a0 100644 --- a/server/public/plugin/api_timer_layer_generated.go +++ b/server/public/plugin/api_timer_layer_generated.go @@ -160,6 +160,13 @@ func (api *apiTimerLayer) GetUsers(options *model.UserGetOptions) ([]*model.User return _returnsA, _returnsB } +func (api *apiTimerLayer) GetUsersByIds(userIDs []string) ([]*model.User, *model.AppError) { + startTime := timePkg.Now() + _returnsA, _returnsB := api.apiImpl.GetUsersByIds(userIDs) + api.recordTime(startTime, "GetUsersByIds", _returnsB == nil) + return _returnsA, _returnsB +} + func (api *apiTimerLayer) GetUser(userID string) (*model.User, *model.AppError) { startTime := timePkg.Now() _returnsA, _returnsB := api.apiImpl.GetUser(userID) diff --git a/server/public/plugin/client_rpc_generated.go b/server/public/plugin/client_rpc_generated.go index ea1905fa76..a2fa59c9de 100644 --- a/server/public/plugin/client_rpc_generated.go +++ b/server/public/plugin/client_rpc_generated.go @@ -1665,6 +1665,35 @@ func (s *apiRPCServer) GetUsers(args *Z_GetUsersArgs, returns *Z_GetUsersReturns return nil } +type Z_GetUsersByIdsArgs struct { + A []string +} + +type Z_GetUsersByIdsReturns struct { + A []*model.User + B *model.AppError +} + +func (g *apiRPCClient) GetUsersByIds(userIDs []string) ([]*model.User, *model.AppError) { + _args := &Z_GetUsersByIdsArgs{userIDs} + _returns := &Z_GetUsersByIdsReturns{} + if err := g.client.Call("Plugin.GetUsersByIds", _args, _returns); err != nil { + log.Printf("RPC call to GetUsersByIds API failed: %s", err.Error()) + } + return _returns.A, _returns.B +} + +func (s *apiRPCServer) GetUsersByIds(args *Z_GetUsersByIdsArgs, returns *Z_GetUsersByIdsReturns) error { + if hook, ok := s.impl.(interface { + GetUsersByIds(userIDs []string) ([]*model.User, *model.AppError) + }); ok { + returns.A, returns.B = hook.GetUsersByIds(args.A) + } else { + return encodableError(fmt.Errorf("API GetUsersByIds called but not implemented.")) + } + return nil +} + type Z_GetUserArgs struct { A string } diff --git a/server/public/plugin/plugintest/api.go b/server/public/plugin/plugintest/api.go index 635aed9966..07f212fdae 100644 --- a/server/public/plugin/plugintest/api.go +++ b/server/public/plugin/plugintest/api.go @@ -3064,6 +3064,38 @@ func (_m *API) GetUsers(options *model.UserGetOptions) ([]*model.User, *model.Ap return r0, r1 } +// GetUsersByIds provides a mock function with given fields: userIDs +func (_m *API) GetUsersByIds(userIDs []string) ([]*model.User, *model.AppError) { + ret := _m.Called(userIDs) + + if len(ret) == 0 { + panic("no return value specified for GetUsersByIds") + } + + var r0 []*model.User + var r1 *model.AppError + if rf, ok := ret.Get(0).(func([]string) ([]*model.User, *model.AppError)); ok { + return rf(userIDs) + } + if rf, ok := ret.Get(0).(func([]string) []*model.User); ok { + r0 = rf(userIDs) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*model.User) + } + } + + if rf, ok := ret.Get(1).(func([]string) *model.AppError); ok { + r1 = rf(userIDs) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*model.AppError) + } + } + + return r0, r1 +} + // GetUsersByUsernames provides a mock function with given fields: usernames func (_m *API) GetUsersByUsernames(usernames []string) ([]*model.User, *model.AppError) { ret := _m.Called(usernames) diff --git a/server/public/pluginapi/user.go b/server/public/pluginapi/user.go index 0b1d962b23..e8f72f2e5a 100644 --- a/server/public/pluginapi/user.go +++ b/server/public/pluginapi/user.go @@ -49,6 +49,15 @@ func (u *UserService) List(options *model.UserGetOptions) ([]*model.User, error) return users, normalizeAppErr(appErr) } +// ListByUserIDs gets users by their IDs. +// +// Minimum server version: 9.8 +func (u *UserService) ListByUserIDs(userIDs []string) ([]*model.User, error) { + users, appErr := u.api.GetUsersByIds(userIDs) + + return users, normalizeAppErr(appErr) +} + // ListByUsernames gets users by their usernames. // // Minimum server version: 5.6 diff --git a/server/public/pluginapi/user_test.go b/server/public/pluginapi/user_test.go index beb5138a15..40bbf9d433 100644 --- a/server/public/pluginapi/user_test.go +++ b/server/public/pluginapi/user_test.go @@ -96,6 +96,35 @@ func TestGetUsers(t *testing.T) { }) } +func TestListByUserIDs(t *testing.T) { + t.Run("success", func(t *testing.T) { + api := &plugintest.API{} + defer api.AssertExpectations(t) + client := pluginapi.NewClient(api, &plugintest.Driver{}) + + userIDs := []string{"123"} + expectedUsers := []*model.User{{Id: "123", Username: "test"}} + api.On("GetUsersByIds", userIDs).Return(expectedUsers, nil) + + actualUsers, err := client.User.ListByUserIDs(userIDs) + require.NoError(t, err) + assert.Equal(t, expectedUsers, actualUsers) + }) + + t.Run("failure", func(t *testing.T) { + api := &plugintest.API{} + defer api.AssertExpectations(t) + client := pluginapi.NewClient(api, &plugintest.Driver{}) + + userIDs := []string{"123"} + api.On("GetUsersByIds", userIDs).Return(nil, newAppError()) + + actualUsers, err := client.User.ListByUserIDs(userIDs) + require.EqualError(t, err, "here: id, an error occurred") + assert.Nil(t, actualUsers) + }) +} + func TestGetUser(t *testing.T) { t.Run("success", func(t *testing.T) { api := &plugintest.API{}