MM-45120: Track team join times. Add API to retrieve new team members since a given time. (#20708)
* MM-45120: Starts tracking team join time. Adds API to retrieve team members who joined after a given time. * MM-45120: Updates json casing to match model.User.
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
1738bd6e92
Коммит
55b3961b98
@@ -23,6 +23,9 @@ func (api *API) InitInsights() {
|
||||
// Threads
|
||||
api.BaseRoutes.InsightsForTeam.Handle("/threads", api.APISessionRequired(requireLicense(getTopThreadsForTeamSince))).Methods("GET")
|
||||
api.BaseRoutes.InsightsForUser.Handle("/threads", api.APISessionRequired(requireLicense(getTopThreadsForUserSince))).Methods("GET")
|
||||
|
||||
// New teammembers
|
||||
api.BaseRoutes.InsightsForTeam.Handle("/team_members", api.APISessionRequired(minimumProfessionalLicense(rejectGuests(getNewTeamMembersSince)))).Methods("GET")
|
||||
}
|
||||
|
||||
// Top Reactions
|
||||
@@ -357,3 +360,49 @@ func postCountByDurationViewModel(c *Context, topChannelList *model.TopChannelLi
|
||||
}
|
||||
return model.ToDailyPostCountViewModel(postCountsByDay, startTime, model.TimeRangeToNumberDays(timeRange), channelIDs), nil
|
||||
}
|
||||
|
||||
func getNewTeamMembersSince(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
c.RequireTeamId()
|
||||
if c.Err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
team, err := c.App.GetTeam(c.Params.TeamId)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), team.Id, model.PermissionViewTeam) {
|
||||
c.SetPermissionError(model.PermissionViewTeam)
|
||||
return
|
||||
}
|
||||
|
||||
user, err := c.App.GetUser(c.AppContext.Session().UserId)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
loc := user.GetTimezoneLocation()
|
||||
startTime := model.StartOfDayForTimeRange(c.Params.TimeRange, loc)
|
||||
|
||||
ntms, count, err := c.App.GetNewTeamMembersSince(c.AppContext, c.Params.TeamId, &model.InsightsOpts{
|
||||
StartUnixMilli: startTime.UnixMilli(),
|
||||
Page: c.Params.Page,
|
||||
PerPage: c.Params.PerPage,
|
||||
})
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
ntms.TotalCount = count
|
||||
|
||||
js, jsonErr := json.Marshal(ntms)
|
||||
if jsonErr != nil {
|
||||
c.Err = model.NewAppError("getNewTeamembersForTeamSince", "api.marshal_error", nil, jsonErr.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Write(js)
|
||||
}
|
||||
|
||||
@@ -816,3 +816,86 @@ func TestGetTopThreadsForUserSince(t *testing.T) {
|
||||
require.Nil(t, appErr)
|
||||
require.Len(t, topUser2ThreadsAfterPrivateReplyDelete.Items, 0)
|
||||
}
|
||||
|
||||
func TestNewTeamMembersSince(t *testing.T) {
|
||||
th := Setup(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
th.LoginBasic()
|
||||
|
||||
team := th.CreateTeam()
|
||||
|
||||
t.Run("accepts only starter or professional license skus", func(t *testing.T) {
|
||||
_, resp, _ := th.Client.GetNewTeamMembersSince(team.Id, model.TimeRangeToday, 0, 5)
|
||||
CheckNotImplementedStatus(t, resp)
|
||||
|
||||
th.App.Srv().SetLicense(model.NewTestLicenseSKU(model.LicenseShortSkuE10))
|
||||
_, resp, _ = th.Client.GetNewTeamMembersSince(team.Id, model.TimeRangeToday, 0, 5)
|
||||
CheckNotImplementedStatus(t, resp)
|
||||
|
||||
th.App.Srv().SetLicense(model.NewTestLicenseSKU(model.LicenseShortSkuE20))
|
||||
_, resp, _ = th.Client.GetNewTeamMembersSince(team.Id, model.TimeRangeToday, 0, 5)
|
||||
CheckNotImplementedStatus(t, resp)
|
||||
|
||||
th.App.Srv().SetLicense(model.NewTestLicenseSKU(model.LicenseShortSkuProfessional))
|
||||
_, resp, err := th.Client.GetNewTeamMembersSince(team.Id, model.TimeRangeToday, 0, 5)
|
||||
require.NoError(t, err)
|
||||
CheckOKStatus(t, resp)
|
||||
|
||||
th.App.Srv().SetLicense(model.NewTestLicenseSKU(model.LicenseShortSkuEnterprise))
|
||||
_, resp, err = th.Client.GetNewTeamMembersSince(team.Id, model.TimeRangeToday, 0, 5)
|
||||
require.NoError(t, err)
|
||||
CheckOKStatus(t, resp)
|
||||
})
|
||||
|
||||
t.Run("rejects guests", func(t *testing.T) {
|
||||
_, resp, err := th.Client.GetNewTeamMembersSince(team.Id, model.TimeRangeToday, 0, 5)
|
||||
require.NoError(t, err)
|
||||
CheckOKStatus(t, resp)
|
||||
|
||||
th.App.DemoteUserToGuest(th.Context, th.BasicUser)
|
||||
defer th.App.PromoteGuestToUser(th.Context, th.BasicUser, "")
|
||||
|
||||
_, resp, _ = th.Client.GetNewTeamMembersSince(team.Id, model.TimeRangeToday, 0, 5)
|
||||
CheckNotImplementedStatus(t, resp)
|
||||
})
|
||||
|
||||
t.Run("implements pagination", func(t *testing.T) {
|
||||
// check the first page of results
|
||||
list, resp, err := th.Client.GetNewTeamMembersSince(team.Id, model.TimeRangeToday, 0, 2)
|
||||
require.NoError(t, err)
|
||||
CheckOKStatus(t, resp)
|
||||
|
||||
require.Equal(t, int(list.TotalCount), 1)
|
||||
require.Len(t, list.Items, 1)
|
||||
require.False(t, list.HasNext)
|
||||
|
||||
// check the 2nd page
|
||||
list, resp, err = th.Client.GetNewTeamMembersSince(team.Id, model.TimeRangeToday, 1, 2)
|
||||
require.NoError(t, err)
|
||||
CheckOKStatus(t, resp)
|
||||
|
||||
require.GreaterOrEqual(t, len(list.Items), 0)
|
||||
|
||||
// add a few new team members and re-test the pagination
|
||||
user := th.CreateUser()
|
||||
_, appErr := th.App.AddTeamMember(th.Context, team.Id, th.BasicUser2.Id)
|
||||
require.Nil(t, appErr)
|
||||
_, appErr = th.App.AddTeamMember(th.Context, team.Id, user.Id)
|
||||
require.Nil(t, appErr)
|
||||
|
||||
list, resp, err = th.Client.GetNewTeamMembersSince(team.Id, model.TimeRangeToday, 0, 2)
|
||||
require.NoError(t, err)
|
||||
CheckOKStatus(t, resp)
|
||||
require.Equal(t, 3, int(list.TotalCount))
|
||||
require.Len(t, list.Items, 2)
|
||||
require.True(t, list.HasNext)
|
||||
|
||||
list, resp, err = th.Client.GetNewTeamMembersSince(team.Id, model.TimeRangeToday, 1, 2)
|
||||
require.NoError(t, err)
|
||||
CheckOKStatus(t, resp)
|
||||
require.Equal(t, int(list.TotalCount), 3)
|
||||
require.Len(t, list.Items, 1)
|
||||
require.False(t, list.HasNext)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -670,6 +670,7 @@ type AppIface interface {
|
||||
GetMemberCountsByGroup(ctx context.Context, channelID string, includeTimezones bool) ([]*model.ChannelMemberCountByGroup, *model.AppError)
|
||||
GetMessageForNotification(post *model.Post, translateFunc i18n.TranslateFunc) string
|
||||
GetMultipleEmojiByName(names []string) ([]*model.Emoji, *model.AppError)
|
||||
GetNewTeamMembersSince(c request.CTX, teamID string, opts *model.InsightsOpts) (*model.NewTeamMembersList, int64, *model.AppError)
|
||||
GetNewUsersForTeamPage(teamID string, page, perPage int, asAdmin bool, viewRestrictions *model.ViewUsersRestrictions) ([]*model.User, *model.AppError)
|
||||
GetNextPostIdFromPostList(postList *model.PostList, collapsedThreads bool) string
|
||||
GetNotificationNameFormat(user *model.User) string
|
||||
|
||||
@@ -805,6 +805,7 @@ func (a *App) importUserTeams(c request.CTX, user *model.User, data *[]UserTeamI
|
||||
SchemeGuest: user.IsGuest(),
|
||||
SchemeUser: !user.IsGuest(),
|
||||
SchemeAdmin: team.Email == user.Email && !user.IsGuest(),
|
||||
CreateAt: model.GetMillis(),
|
||||
}
|
||||
if !user.IsGuest() {
|
||||
var userShouldBeAdmin bool
|
||||
|
||||
@@ -7045,6 +7045,28 @@ func (a *OpenTracingAppLayer) GetMultipleEmojiByName(names []string) ([]*model.E
|
||||
return resultVar0, resultVar1
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) GetNewTeamMembersSince(c request.CTX, teamID string, opts *model.InsightsOpts) (*model.NewTeamMembersList, int64, *model.AppError) {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetNewTeamMembersSince")
|
||||
|
||||
a.ctx = newCtx
|
||||
a.app.Srv().Store.SetContext(newCtx)
|
||||
defer func() {
|
||||
a.app.Srv().Store.SetContext(origCtx)
|
||||
a.ctx = origCtx
|
||||
}()
|
||||
|
||||
defer span.Finish()
|
||||
resultVar0, resultVar1, resultVar2 := a.app.GetNewTeamMembersSince(c, teamID, opts)
|
||||
|
||||
if resultVar2 != nil {
|
||||
span.LogFields(spanlog.Error(resultVar2))
|
||||
ext.Error.Set(span, true)
|
||||
}
|
||||
|
||||
return resultVar0, resultVar1, resultVar2
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) GetNewUsersForTeamPage(teamID string, page int, perPage int, asAdmin bool, viewRestrictions *model.ViewUsersRestrictions) ([]*model.User, *model.AppError) {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetNewUsersForTeamPage")
|
||||
|
||||
@@ -59,7 +59,7 @@ func CreateBasicUser(a *app.App, client *model.Client4) error {
|
||||
if err != nil {
|
||||
return model.NewAppError("CreateBasicUser", "app.user.verify_email.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
if _, nErr := a.Srv().Store.Team().SaveMember(&model.TeamMember{TeamId: basicteam.Id, UserId: ruser.Id}, *a.Config().TeamSettings.MaxUsersPerTeam); nErr != nil {
|
||||
if _, nErr := a.Srv().Store.Team().SaveMember(&model.TeamMember{TeamId: basicteam.Id, UserId: ruser.Id, CreateAt: model.GetMillis()}, *a.Config().TeamSettings.MaxUsersPerTeam); nErr != nil {
|
||||
var appErr *model.AppError
|
||||
var conflictErr *store.ErrConflict
|
||||
var limitExceededErr *store.ErrLimitExceeded
|
||||
|
||||
13
app/team.go
13
app/team.go
@@ -2133,3 +2133,16 @@ func (a *App) ClearTeamMembersCache(teamID string) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *App) GetNewTeamMembersSince(c request.CTX, teamID string, opts *model.InsightsOpts) (*model.NewTeamMembersList, int64, *model.AppError) {
|
||||
if !a.Config().FeatureFlags.InsightsEnabled {
|
||||
return nil, 0, model.NewAppError("GetNewTeamMembersSince", "app.insights.feature_disabled", nil, "", http.StatusNotImplemented)
|
||||
}
|
||||
|
||||
ntms, count, err := a.Srv().Store.Team().GetNewTeamMembersSince(teamID, opts.StartUnixMilli, opts.Page*opts.PerPage, opts.PerPage)
|
||||
if err != nil {
|
||||
return nil, 0, model.NewAppError("GetNewTeamMembersSince", "app.post.get_new_team_members_since.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
return ntms, count, nil
|
||||
}
|
||||
|
||||
190
app/team_test.go
190
app/team_test.go
@@ -1594,3 +1594,193 @@ func TestInviteGuestsToChannelsGracefully(t *testing.T) {
|
||||
require.NotNil(t, res[0].Error)
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetNewTeamMembersSince(t *testing.T) {
|
||||
th := Setup(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
team := th.CreateTeam()
|
||||
|
||||
t.Run("counts team members", func(t *testing.T) {
|
||||
var originalExpectedCount int64
|
||||
var newTeamMemberJoinTime int64
|
||||
var anotherUser *model.User
|
||||
|
||||
t.Run("since time 0", func(t *testing.T) {
|
||||
teamMembers, err := th.App.Srv().Store.Team().GetMembers(team.Id, 0, 1000, nil)
|
||||
require.NoError(t, err)
|
||||
originalExpectedCount = int64(len(teamMembers))
|
||||
_, actualCount, appErr := th.App.GetNewTeamMembersSince(th.Context, team.Id, &model.InsightsOpts{StartUnixMilli: 0, Page: 0, PerPage: 1000})
|
||||
require.Nil(t, appErr)
|
||||
require.Equal(t, originalExpectedCount, actualCount)
|
||||
})
|
||||
|
||||
t.Run("after a new team member was added", func(t *testing.T) {
|
||||
anotherUser = th.CreateUser()
|
||||
newTeamMember, appErr := th.App.JoinUserToTeam(th.Context, team, anotherUser, "")
|
||||
newTeamMemberJoinTime = newTeamMember.CreateAt
|
||||
require.Nil(t, appErr)
|
||||
_, actualCount, appErr := th.App.GetNewTeamMembersSince(th.Context, team.Id, &model.InsightsOpts{StartUnixMilli: 0, Page: 0, PerPage: 1000})
|
||||
require.Nil(t, appErr)
|
||||
require.Equal(t, originalExpectedCount+1, actualCount)
|
||||
})
|
||||
|
||||
t.Run("after a team member was added to a different team, ensuring the wrong team's member count isn't incremented", func(t *testing.T) {
|
||||
anotherUser2 := th.CreateUser()
|
||||
anotherTeam := th.CreateTeam()
|
||||
_, appErr := th.App.JoinUserToTeam(th.Context, anotherTeam, anotherUser2, "")
|
||||
require.Nil(t, appErr)
|
||||
_, actualCount, appErr := th.App.GetNewTeamMembersSince(th.Context, team.Id, &model.InsightsOpts{StartUnixMilli: 0, Page: 0, PerPage: 1000})
|
||||
require.Nil(t, appErr)
|
||||
require.Equal(t, originalExpectedCount+1, actualCount)
|
||||
})
|
||||
|
||||
t.Run("since a given time", func(t *testing.T) {
|
||||
_, actualCount, appErr := th.App.GetNewTeamMembersSince(th.Context, team.Id, &model.InsightsOpts{StartUnixMilli: newTeamMemberJoinTime, Page: 0, PerPage: 1000})
|
||||
require.Nil(t, appErr)
|
||||
require.Equal(t, int64(1), actualCount)
|
||||
})
|
||||
|
||||
t.Run("after a team member was removed", func(t *testing.T) {
|
||||
th.RemoveUserFromTeam(anotherUser, team)
|
||||
_, actualCount, appErr := th.App.GetNewTeamMembersSince(th.Context, team.Id, &model.InsightsOpts{StartUnixMilli: 0, Page: 0, PerPage: 1000})
|
||||
require.Nil(t, appErr)
|
||||
require.Equal(t, originalExpectedCount, actualCount)
|
||||
})
|
||||
|
||||
t.Run("after a user was deactivated", func(t *testing.T) {
|
||||
_, appErr := th.App.JoinUserToTeam(th.Context, team, anotherUser, "")
|
||||
require.Nil(t, appErr)
|
||||
_, beforeCount, appErr := th.App.GetNewTeamMembersSince(th.Context, team.Id, &model.InsightsOpts{StartUnixMilli: 0, Page: 0, PerPage: 1000})
|
||||
require.Nil(t, appErr)
|
||||
_, appErr = th.App.UpdateActive(th.Context, anotherUser, false)
|
||||
defer th.App.UpdateActive(th.Context, anotherUser, true)
|
||||
require.Nil(t, appErr)
|
||||
_, afterCount, appErr := th.App.GetNewTeamMembersSince(th.Context, team.Id, &model.InsightsOpts{StartUnixMilli: 0, Page: 0, PerPage: 1000})
|
||||
require.Nil(t, appErr)
|
||||
require.Equal(t, beforeCount-1, afterCount)
|
||||
})
|
||||
|
||||
t.Run("after a user was permanently deleted", func(t *testing.T) {
|
||||
_, beforeCount, appErr := th.App.GetNewTeamMembersSince(th.Context, team.Id, &model.InsightsOpts{StartUnixMilli: 0, Page: 0, PerPage: 1000})
|
||||
require.Nil(t, appErr)
|
||||
appErr = th.App.PermanentDeleteUser(th.Context, anotherUser)
|
||||
require.Nil(t, appErr)
|
||||
_, afterCount, appErr := th.App.GetNewTeamMembersSince(th.Context, team.Id, &model.InsightsOpts{StartUnixMilli: 0, Page: 0, PerPage: 1000})
|
||||
require.Nil(t, appErr)
|
||||
require.Equal(t, beforeCount-1, afterCount)
|
||||
})
|
||||
|
||||
t.Run("exclude bots", func(t *testing.T) {
|
||||
user := th.CreateUser()
|
||||
_, appErr := th.App.ConvertUserToBot(user)
|
||||
require.Nil(t, appErr)
|
||||
_, appErr = th.App.JoinUserToTeam(th.Context, team, user, "")
|
||||
require.Nil(t, appErr)
|
||||
_, actualCount, appErr := th.App.GetNewTeamMembersSince(th.Context, team.Id, &model.InsightsOpts{StartUnixMilli: 0, Page: 0, PerPage: 1000})
|
||||
require.Nil(t, appErr)
|
||||
require.Equal(t, originalExpectedCount, actualCount)
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("returns the correct team members", func(t *testing.T) {
|
||||
var originalExpectedMembers []*model.TeamMember
|
||||
var newTeamMemberJoinTime int64
|
||||
var anotherUser *model.User
|
||||
|
||||
uIDs := func(members []*model.TeamMember) []string {
|
||||
ids := []string{}
|
||||
for _, member := range members {
|
||||
ids = append(ids, member.UserId)
|
||||
}
|
||||
return ids
|
||||
}
|
||||
|
||||
nUIDs := func(members []*model.NewTeamMember) []string {
|
||||
ids := []string{}
|
||||
for _, member := range members {
|
||||
ids = append(ids, member.Id)
|
||||
}
|
||||
return ids
|
||||
}
|
||||
|
||||
t.Run("since time 0", func(t *testing.T) {
|
||||
var err error
|
||||
originalExpectedMembers, err = th.App.Srv().Store.Team().GetMembers(th.BasicTeam.Id, 0, 1000, nil)
|
||||
require.NoError(t, err)
|
||||
actualMembersList, _, appErr := th.App.GetNewTeamMembersSince(th.Context, th.BasicTeam.Id, &model.InsightsOpts{StartUnixMilli: 0, Page: 0, PerPage: 1000})
|
||||
require.Nil(t, appErr)
|
||||
require.ElementsMatch(t, uIDs(originalExpectedMembers), nUIDs(actualMembersList.Items))
|
||||
})
|
||||
|
||||
t.Run("after a new team member was added", func(t *testing.T) {
|
||||
anotherUser = th.CreateUser()
|
||||
newTeamMember, appErr := th.App.JoinUserToTeam(th.Context, th.BasicTeam, anotherUser, "")
|
||||
newTeamMemberJoinTime = newTeamMember.CreateAt
|
||||
require.Nil(t, appErr)
|
||||
actualMembersList, _, appErr := th.App.GetNewTeamMembersSince(th.Context, th.BasicTeam.Id, &model.InsightsOpts{StartUnixMilli: 0, Page: 0, PerPage: 1000})
|
||||
require.Nil(t, appErr)
|
||||
require.ElementsMatch(t, append(uIDs(originalExpectedMembers), anotherUser.Id), nUIDs(actualMembersList.Items))
|
||||
})
|
||||
|
||||
t.Run("after a team member was added to a different team, ensuring the wrong team's member count isn't incremented", func(t *testing.T) {
|
||||
anotherUser2 := th.CreateUser()
|
||||
anotherTeam := th.CreateTeam()
|
||||
_, appErr := th.App.JoinUserToTeam(th.Context, anotherTeam, anotherUser2, "")
|
||||
require.Nil(t, appErr)
|
||||
actualMembersList, _, appErr := th.App.GetNewTeamMembersSince(th.Context, th.BasicTeam.Id, &model.InsightsOpts{StartUnixMilli: 0, Page: 0, PerPage: 1000})
|
||||
require.Nil(t, appErr)
|
||||
require.ElementsMatch(t, append(uIDs(originalExpectedMembers), anotherUser.Id), nUIDs(actualMembersList.Items))
|
||||
})
|
||||
|
||||
t.Run("since a given time", func(t *testing.T) {
|
||||
actualMembersList, _, appErr := th.App.GetNewTeamMembersSince(th.Context, th.BasicTeam.Id, &model.InsightsOpts{StartUnixMilli: newTeamMemberJoinTime, Page: 0, PerPage: 1000})
|
||||
require.Nil(t, appErr)
|
||||
require.Len(t, actualMembersList.Items, 1)
|
||||
require.Equal(t, anotherUser.Id, actualMembersList.Items[0].Id)
|
||||
})
|
||||
|
||||
t.Run("after a team member was removed", func(t *testing.T) {
|
||||
th.RemoveUserFromTeam(anotherUser, th.BasicTeam)
|
||||
actualMembersList, _, appErr := th.App.GetNewTeamMembersSince(th.Context, th.BasicTeam.Id, &model.InsightsOpts{StartUnixMilli: 0, Page: 0, PerPage: 1000})
|
||||
require.Nil(t, appErr)
|
||||
require.ElementsMatch(t, uIDs(originalExpectedMembers), nUIDs(actualMembersList.Items))
|
||||
})
|
||||
|
||||
t.Run("after a user was deactivated", func(t *testing.T) {
|
||||
_, appErr := th.App.JoinUserToTeam(th.Context, th.BasicTeam, anotherUser, "")
|
||||
require.Nil(t, appErr)
|
||||
beforeMembersList, _, appErr := th.App.GetNewTeamMembersSince(th.Context, th.BasicTeam.Id, &model.InsightsOpts{StartUnixMilli: 0, Page: 0, PerPage: 1000})
|
||||
require.Nil(t, appErr)
|
||||
require.Contains(t, nUIDs(beforeMembersList.Items), anotherUser.Id)
|
||||
_, appErr = th.App.UpdateActive(th.Context, anotherUser, false)
|
||||
defer th.App.UpdateActive(th.Context, anotherUser, true)
|
||||
require.Nil(t, appErr)
|
||||
afterMembersList, _, appErr := th.App.GetNewTeamMembersSince(th.Context, th.BasicTeam.Id, &model.InsightsOpts{StartUnixMilli: 0, Page: 0, PerPage: 1000})
|
||||
require.Nil(t, appErr)
|
||||
require.NotContains(t, nUIDs(afterMembersList.Items), anotherUser.Id)
|
||||
})
|
||||
|
||||
t.Run("after a user was permanently deleted", func(t *testing.T) {
|
||||
beforeMembersList, _, appErr := th.App.GetNewTeamMembersSince(th.Context, th.BasicTeam.Id, &model.InsightsOpts{StartUnixMilli: 0, Page: 0, PerPage: 1000})
|
||||
require.Nil(t, appErr)
|
||||
require.Contains(t, nUIDs(beforeMembersList.Items), anotherUser.Id)
|
||||
appErr = th.App.PermanentDeleteUser(th.Context, anotherUser)
|
||||
require.Nil(t, appErr)
|
||||
afterMembersList, _, appErr := th.App.GetNewTeamMembersSince(th.Context, th.BasicTeam.Id, &model.InsightsOpts{StartUnixMilli: 0, Page: 0, PerPage: 1000})
|
||||
require.Nil(t, appErr)
|
||||
require.NotContains(t, nUIDs(afterMembersList.Items), anotherUser.Id)
|
||||
})
|
||||
|
||||
t.Run("exclude bots", func(t *testing.T) {
|
||||
user := th.CreateUser()
|
||||
_, appErr := th.App.ConvertUserToBot(user)
|
||||
require.Nil(t, appErr)
|
||||
_, appErr = th.App.JoinUserToTeam(th.Context, th.BasicTeam, user, "")
|
||||
require.Nil(t, appErr)
|
||||
actualMembersList, _, appErr := th.App.GetNewTeamMembersSince(th.Context, th.BasicTeam.Id, &model.InsightsOpts{StartUnixMilli: 0, Page: 0, PerPage: 1000})
|
||||
require.Nil(t, appErr)
|
||||
require.ElementsMatch(t, uIDs(originalExpectedMembers), nUIDs(actualMembersList.Items))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -141,6 +141,7 @@ func (ts *TeamService) JoinUserToTeam(team *model.Team, user *model.User) (*mode
|
||||
UserId: user.Id,
|
||||
SchemeGuest: user.IsGuest(),
|
||||
SchemeUser: !user.IsGuest(),
|
||||
CreateAt: model.GetMillis(),
|
||||
}
|
||||
|
||||
if !user.IsGuest() {
|
||||
|
||||
@@ -182,6 +182,8 @@ db/migrations/mysql/000090_create_enums.down.sql
|
||||
db/migrations/mysql/000090_create_enums.up.sql
|
||||
db/migrations/mysql/000091_create_post_reminder.down.sql
|
||||
db/migrations/mysql/000091_create_post_reminder.up.sql
|
||||
db/migrations/mysql/000092_add_createat_to_teammembers.down.sql
|
||||
db/migrations/mysql/000092_add_createat_to_teammembers.up.sql
|
||||
db/migrations/postgres/000001_create_teams.down.sql
|
||||
db/migrations/postgres/000001_create_teams.up.sql
|
||||
db/migrations/postgres/000002_create_team_members.down.sql
|
||||
@@ -364,3 +366,5 @@ db/migrations/postgres/000090_create_enums.down.sql
|
||||
db/migrations/postgres/000090_create_enums.up.sql
|
||||
db/migrations/postgres/000091_create_post_reminder.down.sql
|
||||
db/migrations/postgres/000091_create_post_reminder.up.sql
|
||||
db/migrations/postgres/000092_add_createat_to_teamembers.down.sql
|
||||
db/migrations/postgres/000092_add_createat_to_teamembers.up.sql
|
||||
|
||||
14
db/migrations/mysql/000092_add_createat_to_teammembers.down.sql
Обычный файл
14
db/migrations/mysql/000092_add_createat_to_teammembers.down.sql
Обычный файл
@@ -0,0 +1,14 @@
|
||||
SET @preparedStatement = (SELECT IF(
|
||||
EXISTS(
|
||||
SELECT 1 FROM INFORMATION_SCHEMA.STATISTICS
|
||||
WHERE table_name = 'TeamMembers'
|
||||
AND table_schema = DATABASE()
|
||||
AND column_name = 'CreateAt'
|
||||
) > 0,
|
||||
'ALTER TABLE Reactions DROP COLUMN CreateAt;',
|
||||
'SELECT 1;'
|
||||
));
|
||||
|
||||
PREPARE removeColumnIfExists FROM @preparedStatement;
|
||||
EXECUTE removeColumnIfExists;
|
||||
DEALLOCATE PREPARE removeColumnIfExists;
|
||||
29
db/migrations/mysql/000092_add_createat_to_teammembers.up.sql
Обычный файл
29
db/migrations/mysql/000092_add_createat_to_teammembers.up.sql
Обычный файл
@@ -0,0 +1,29 @@
|
||||
SET @preparedStatement = (SELECT IF(
|
||||
NOT EXISTS(
|
||||
SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE table_name = 'TeamMembers'
|
||||
AND table_schema = DATABASE()
|
||||
AND column_name = 'CreateAt'
|
||||
),
|
||||
'ALTER TABLE TeamMembers ADD COLUMN CreateAt bigint DEFAULT 0;',
|
||||
'SELECT 1;'
|
||||
));
|
||||
|
||||
PREPARE addColumnIfNotExists FROM @preparedStatement;
|
||||
EXECUTE addColumnIfNotExists;
|
||||
DEALLOCATE PREPARE addColumnIfNotExists;
|
||||
|
||||
SET @preparedStatement = (SELECT IF(
|
||||
(
|
||||
SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS
|
||||
WHERE table_name = 'TeamMembers'
|
||||
AND table_schema = DATABASE()
|
||||
AND index_name = 'idx_teammembers_create_at'
|
||||
) > 0,
|
||||
'SELECT 1',
|
||||
'CREATE INDEX idx_teammembers_createat ON TeamMembers(CreateAt);'
|
||||
));
|
||||
|
||||
PREPARE createIndexIfNotExists FROM @preparedStatement;
|
||||
EXECUTE createIndexIfNotExists;
|
||||
DEALLOCATE PREPARE createIndexIfNotExists;
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE teammembers DROP COLUMN IF EXISTS createat;
|
||||
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE teammembers ADD COLUMN IF NOT EXISTS createat bigint DEFAULT 0;
|
||||
CREATE INDEX IF NOT EXISTS idx_teammembers_createat on teammembers (createat);
|
||||
@@ -5835,6 +5835,10 @@
|
||||
"id": "app.post.get_flagged_posts.app_error",
|
||||
"translation": "Unable to get the flagged posts."
|
||||
},
|
||||
{
|
||||
"id": "app.post.get_new_team_members_since.app_error",
|
||||
"translation": " "
|
||||
},
|
||||
{
|
||||
"id": "app.post.get_post_after_time.app_error",
|
||||
"translation": "Unable to get post after time bound."
|
||||
|
||||
@@ -8354,3 +8354,17 @@ func (c *Client4) GetIntegrationsUsage() (*IntegrationsUsage, *Response, error)
|
||||
err = json.NewDecoder(r.Body).Decode(&usage)
|
||||
return usage, BuildResponse(r), err
|
||||
}
|
||||
|
||||
func (c *Client4) GetNewTeamMembersSince(teamID string, timeRange string, page int, perPage int) (*NewTeamMembersList, *Response, error) {
|
||||
query := fmt.Sprintf("?time_range=%v&page=%v&per_page=%v", timeRange, page, perPage)
|
||||
r, err := c.DoAPIGet(c.teamRoute(teamID)+"/top/team_members"+query, "")
|
||||
if err != nil {
|
||||
return nil, BuildResponse(r), err
|
||||
}
|
||||
defer closeBody(r)
|
||||
var newTeamMembersList *NewTeamMembersList
|
||||
if jsonErr := json.NewDecoder(r.Body).Decode(&newTeamMembersList); jsonErr != nil {
|
||||
return nil, nil, NewAppError("GetNewTeamMembersSince", "api.unmarshal_error", nil, jsonErr.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
return newTeamMembersList, BuildResponse(r), nil
|
||||
}
|
||||
|
||||
@@ -90,9 +90,25 @@ type InsightUserInformation struct {
|
||||
Username string `json:"username"`
|
||||
}
|
||||
|
||||
type NewTeamMembersList struct {
|
||||
InsightsListData
|
||||
Items []*NewTeamMember `json:"items"`
|
||||
TotalCount int64 `json:"total_count"`
|
||||
}
|
||||
|
||||
type NewTeamMember struct {
|
||||
Id string `json:"id"`
|
||||
Username string `json:"username"`
|
||||
FirstName string `json:"first_name"`
|
||||
LastName string `json:"last_name"`
|
||||
Position string `json:"position"`
|
||||
Nickname string `json:"nickname"`
|
||||
CreateAt int64 `json:"create_at"`
|
||||
}
|
||||
|
||||
type DurationPostCount struct {
|
||||
ChannelID string `db:"channelid"`
|
||||
// Duration is an ISO8601 date string representing either a day or a day and hour (ex. "2022-05-26" or "2022-05-26T14").
|
||||
// Duration is an ISO8601 date string.
|
||||
Duration string `db:"duration"`
|
||||
PostCount int `db:"postcount"`
|
||||
}
|
||||
@@ -243,3 +259,13 @@ func GetTopThreadListWithPagination(threads []*TopThread, limit int) *TopThreadL
|
||||
|
||||
return &TopThreadList{InsightsListData: InsightsListData{HasNext: hasNext}, Items: threads}
|
||||
}
|
||||
|
||||
func GetNewTeamMembersListWithPagination(teamMembers []*NewTeamMember, limit int) *NewTeamMembersList {
|
||||
var hasNext bool
|
||||
if (limit != 0) && (len(teamMembers) == limit+1) {
|
||||
hasNext = true
|
||||
teamMembers = teamMembers[:len(teamMembers)-1]
|
||||
}
|
||||
|
||||
return &NewTeamMembersList{InsightsListData: InsightsListData{HasNext: hasNext}, Items: teamMembers}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ type TeamMember struct {
|
||||
SchemeUser bool `json:"scheme_user"`
|
||||
SchemeAdmin bool `json:"scheme_admin"`
|
||||
ExplicitRoles string `json:"explicit_roles"`
|
||||
CreateAt int64 `json:"-"`
|
||||
}
|
||||
|
||||
func (o *TeamMember) Auditable() map[string]interface{} {
|
||||
@@ -37,6 +38,7 @@ func (o *TeamMember) Auditable() map[string]interface{} {
|
||||
"scheme_user": o.SchemeUser,
|
||||
"scheme_admin": o.SchemeAdmin,
|
||||
"explicit_roles": o.ExplicitRoles,
|
||||
"create_at": o.CreateAt,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,8 +17,8 @@ func (z *TeamMember) DecodeMsg(dc *msgp.Reader) (err error) {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
if zb0001 != 8 {
|
||||
err = msgp.ArrayError{Wanted: 8, Got: zb0001}
|
||||
if zb0001 != 9 {
|
||||
err = msgp.ArrayError{Wanted: 9, Got: zb0001}
|
||||
return
|
||||
}
|
||||
z.TeamId, err = dc.ReadString()
|
||||
@@ -61,13 +61,18 @@ func (z *TeamMember) DecodeMsg(dc *msgp.Reader) (err error) {
|
||||
err = msgp.WrapError(err, "ExplicitRoles")
|
||||
return
|
||||
}
|
||||
z.CreateAt, err = dc.ReadInt64()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "CreateAt")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// EncodeMsg implements msgp.Encodable
|
||||
func (z *TeamMember) EncodeMsg(en *msgp.Writer) (err error) {
|
||||
// array header, size 8
|
||||
err = en.Append(0x98)
|
||||
// array header, size 9
|
||||
err = en.Append(0x99)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -111,14 +116,19 @@ func (z *TeamMember) EncodeMsg(en *msgp.Writer) (err error) {
|
||||
err = msgp.WrapError(err, "ExplicitRoles")
|
||||
return
|
||||
}
|
||||
err = en.WriteInt64(z.CreateAt)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "CreateAt")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// MarshalMsg implements msgp.Marshaler
|
||||
func (z *TeamMember) MarshalMsg(b []byte) (o []byte, err error) {
|
||||
o = msgp.Require(b, z.Msgsize())
|
||||
// array header, size 8
|
||||
o = append(o, 0x98)
|
||||
// array header, size 9
|
||||
o = append(o, 0x99)
|
||||
o = msgp.AppendString(o, z.TeamId)
|
||||
o = msgp.AppendString(o, z.UserId)
|
||||
o = msgp.AppendString(o, z.Roles)
|
||||
@@ -127,6 +137,7 @@ func (z *TeamMember) MarshalMsg(b []byte) (o []byte, err error) {
|
||||
o = msgp.AppendBool(o, z.SchemeUser)
|
||||
o = msgp.AppendBool(o, z.SchemeAdmin)
|
||||
o = msgp.AppendString(o, z.ExplicitRoles)
|
||||
o = msgp.AppendInt64(o, z.CreateAt)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -138,8 +149,8 @@ func (z *TeamMember) UnmarshalMsg(bts []byte) (o []byte, err error) {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
if zb0001 != 8 {
|
||||
err = msgp.ArrayError{Wanted: 8, Got: zb0001}
|
||||
if zb0001 != 9 {
|
||||
err = msgp.ArrayError{Wanted: 9, Got: zb0001}
|
||||
return
|
||||
}
|
||||
z.TeamId, bts, err = msgp.ReadStringBytes(bts)
|
||||
@@ -182,12 +193,17 @@ func (z *TeamMember) UnmarshalMsg(bts []byte) (o []byte, err error) {
|
||||
err = msgp.WrapError(err, "ExplicitRoles")
|
||||
return
|
||||
}
|
||||
z.CreateAt, bts, err = msgp.ReadInt64Bytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "CreateAt")
|
||||
return
|
||||
}
|
||||
o = bts
|
||||
return
|
||||
}
|
||||
|
||||
// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message
|
||||
func (z *TeamMember) Msgsize() (s int) {
|
||||
s = 1 + msgp.StringPrefixSize + len(z.TeamId) + msgp.StringPrefixSize + len(z.UserId) + msgp.StringPrefixSize + len(z.Roles) + msgp.Int64Size + msgp.BoolSize + msgp.BoolSize + msgp.BoolSize + msgp.StringPrefixSize + len(z.ExplicitRoles)
|
||||
s = 1 + msgp.StringPrefixSize + len(z.TeamId) + msgp.StringPrefixSize + len(z.UserId) + msgp.StringPrefixSize + len(z.Roles) + msgp.Int64Size + msgp.BoolSize + msgp.BoolSize + msgp.BoolSize + msgp.StringPrefixSize + len(z.ExplicitRoles) + msgp.Int64Size
|
||||
return
|
||||
}
|
||||
|
||||
@@ -8967,6 +8967,24 @@ func (s *OpenTracingLayerTeamStore) GetMembersByIds(teamID string, userIds []str
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (s *OpenTracingLayerTeamStore) GetNewTeamMembersSince(teamID string, since int64, offset int, limit int) (*model.NewTeamMembersList, int64, error) {
|
||||
origCtx := s.Root.Store.Context()
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "TeamStore.GetNewTeamMembersSince")
|
||||
s.Root.Store.SetContext(newCtx)
|
||||
defer func() {
|
||||
s.Root.Store.SetContext(origCtx)
|
||||
}()
|
||||
|
||||
defer span.Finish()
|
||||
result, resultVar1, err := s.TeamStore.GetNewTeamMembersSince(teamID, since, offset, limit)
|
||||
if err != nil {
|
||||
span.LogFields(spanlog.Error(err))
|
||||
ext.Error.Set(span, true)
|
||||
}
|
||||
|
||||
return result, resultVar1, err
|
||||
}
|
||||
|
||||
func (s *OpenTracingLayerTeamStore) GetTeamMembersForExport(userID string) ([]*model.TeamMemberForExport, error) {
|
||||
origCtx := s.Root.Store.Context()
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "TeamStore.GetTeamMembersForExport")
|
||||
|
||||
@@ -10241,6 +10241,27 @@ func (s *RetryLayerTeamStore) GetMembersByIds(teamID string, userIds []string, r
|
||||
|
||||
}
|
||||
|
||||
func (s *RetryLayerTeamStore) GetNewTeamMembersSince(teamID string, since int64, offset int, limit int) (*model.NewTeamMembersList, int64, error) {
|
||||
|
||||
tries := 0
|
||||
for {
|
||||
result, resultVar1, err := s.TeamStore.GetNewTeamMembersSince(teamID, since, offset, limit)
|
||||
if err == nil {
|
||||
return result, resultVar1, nil
|
||||
}
|
||||
if !isRepeatableError(err) {
|
||||
return result, resultVar1, err
|
||||
}
|
||||
tries++
|
||||
if tries >= 3 {
|
||||
err = errors.Wrap(err, "giving up after 3 consecutive repeatable transaction failures")
|
||||
return result, resultVar1, err
|
||||
}
|
||||
timepkg.Sleep(100 * timepkg.Millisecond)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (s *RetryLayerTeamStore) GetTeamMembersForExport(userID string) ([]*model.TeamMemberForExport, error) {
|
||||
|
||||
tries := 0
|
||||
|
||||
@@ -35,6 +35,7 @@ type teamMember struct {
|
||||
SchemeUser sql.NullBool
|
||||
SchemeAdmin sql.NullBool
|
||||
SchemeGuest sql.NullBool
|
||||
CreateAt int64
|
||||
}
|
||||
|
||||
func NewTeamMemberFromModel(tm *model.TeamMember) *teamMember {
|
||||
@@ -46,6 +47,7 @@ func NewTeamMemberFromModel(tm *model.TeamMember) *teamMember {
|
||||
SchemeGuest: sql.NullBool{Valid: true, Bool: tm.SchemeGuest},
|
||||
SchemeUser: sql.NullBool{Valid: true, Bool: tm.SchemeUser},
|
||||
SchemeAdmin: sql.NullBool{Valid: true, Bool: tm.SchemeAdmin},
|
||||
CreateAt: tm.CreateAt,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,12 +62,13 @@ type teamMemberWithSchemeRoles struct {
|
||||
TeamSchemeDefaultGuestRole sql.NullString
|
||||
TeamSchemeDefaultUserRole sql.NullString
|
||||
TeamSchemeDefaultAdminRole sql.NullString
|
||||
CreateAt int64
|
||||
}
|
||||
|
||||
type teamMemberWithSchemeRolesList []teamMemberWithSchemeRoles
|
||||
|
||||
func teamMemberSliceColumns() []string {
|
||||
return []string{"TeamId", "UserId", "Roles", "DeleteAt", "SchemeUser", "SchemeAdmin", "SchemeGuest"}
|
||||
return []string{"TeamId", "UserId", "Roles", "DeleteAt", "SchemeUser", "SchemeAdmin", "SchemeGuest", "CreateAt"}
|
||||
}
|
||||
|
||||
func teamMemberToSlice(member *model.TeamMember) []any {
|
||||
@@ -77,6 +80,7 @@ func teamMemberToSlice(member *model.TeamMember) []any {
|
||||
resultSlice = append(resultSlice, member.SchemeUser)
|
||||
resultSlice = append(resultSlice, member.SchemeAdmin)
|
||||
resultSlice = append(resultSlice, member.SchemeGuest)
|
||||
resultSlice = append(resultSlice, member.CreateAt)
|
||||
return resultSlice
|
||||
}
|
||||
|
||||
@@ -187,6 +191,7 @@ func (db teamMemberWithSchemeRoles) ToModel() *model.TeamMember {
|
||||
SchemeUser: rolesResult.schemeUser,
|
||||
SchemeAdmin: rolesResult.schemeAdmin,
|
||||
ExplicitRoles: strings.Join(rolesResult.explicitRoles, " "),
|
||||
CreateAt: db.CreateAt,
|
||||
}
|
||||
return tm
|
||||
}
|
||||
@@ -1646,3 +1651,41 @@ func (s SqlTeamStore) GroupSyncedTeamCount() (int64, error) {
|
||||
|
||||
return count, nil
|
||||
}
|
||||
|
||||
func (s SqlTeamStore) GetNewTeamMembersSince(teamID string, since int64, offset int, limit int) (*model.NewTeamMembersList, int64, error) {
|
||||
builderF := func(selectClause string) sq.SelectBuilder {
|
||||
return s.getQueryBuilder().
|
||||
Select(selectClause).
|
||||
From("TeamMembers").
|
||||
Join("Users ON Users.id = TeamMembers.userid").
|
||||
LeftJoin("Bots ON Bots.userid = Users.id").
|
||||
Where(sq.GtOrEq{"TeamMembers.createat": since}).
|
||||
Where(sq.Eq{"TeamMembers.deleteat": 0, "teamid": teamID, "Users.deleteat": 0, "Bots.userid": nil})
|
||||
}
|
||||
|
||||
countBuilder := builderF("count(*)")
|
||||
query, args, err := countBuilder.ToSql()
|
||||
if err != nil {
|
||||
return nil, 0, errors.Wrap(err, "team_tosql")
|
||||
}
|
||||
var totalCount int64
|
||||
err = s.GetReplicaX().Get(&totalCount, query, args...)
|
||||
if err != nil {
|
||||
return nil, 0, errors.Wrap(err, "failed to count team members since")
|
||||
}
|
||||
|
||||
newTeamMembersBuilder := builderF("Users.Id, Users.Username, Users.FirstName, Users.LastName, Users.Position, TeamMembers.CreateAt, Users.Nickname").
|
||||
Limit(uint64(limit + 1)).
|
||||
Offset(uint64(offset))
|
||||
query, args, err = newTeamMembersBuilder.ToSql()
|
||||
if err != nil {
|
||||
return nil, 0, errors.Wrap(err, "team_tosql")
|
||||
}
|
||||
var ntms []*model.NewTeamMember
|
||||
err = s.GetReplicaX().Select(&ntms, query, args...)
|
||||
if err != nil {
|
||||
return nil, 0, errors.Wrap(err, "failed to get team members since")
|
||||
}
|
||||
|
||||
return model.GetNewTeamMembersListWithPagination(ntms, limit), totalCount, nil
|
||||
}
|
||||
|
||||
@@ -1765,7 +1765,7 @@ func (us SqlUserStore) GetUsersBatchForIndexing(startTime int64, startFileID str
|
||||
|
||||
teamMembers := []*model.TeamMember{}
|
||||
teamMembersQuery, args, _ := us.getQueryBuilder().
|
||||
Select("TeamId, UserId, Roles, DeleteAt, (SchemeGuest IS NOT NULL AND SchemeGuest) as SchemeGuest, SchemeUser, SchemeAdmin").
|
||||
Select("TeamId, UserId, Roles, DeleteAt, CreateAt, (SchemeGuest IS NOT NULL AND SchemeGuest) as SchemeGuest, SchemeUser, SchemeAdmin").
|
||||
From("TeamMembers").
|
||||
Where(sq.Eq{"UserId": userIds, "DeleteAt": 0}).
|
||||
ToSql()
|
||||
|
||||
@@ -166,6 +166,8 @@ type TeamStore interface {
|
||||
// GetCommonTeamIDsForTwoUsers returns the intersection of all the teams to which the specified
|
||||
// users belong.
|
||||
GetCommonTeamIDsForTwoUsers(userID, otherUserID string) ([]string, error)
|
||||
|
||||
GetNewTeamMembersSince(teamID string, since int64, offset int, limit int) (*model.NewTeamMembersList, int64, error)
|
||||
}
|
||||
|
||||
type ChannelStore interface {
|
||||
|
||||
@@ -489,6 +489,36 @@ func (_m *TeamStore) GetMembersByIds(teamID string, userIds []string, restrictio
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// GetNewTeamMembersSince provides a mock function with given fields: teamID, since, offset, limit
|
||||
func (_m *TeamStore) GetNewTeamMembersSince(teamID string, since int64, offset int, limit int) (*model.NewTeamMembersList, int64, error) {
|
||||
ret := _m.Called(teamID, since, offset, limit)
|
||||
|
||||
var r0 *model.NewTeamMembersList
|
||||
if rf, ok := ret.Get(0).(func(string, int64, int, int) *model.NewTeamMembersList); ok {
|
||||
r0 = rf(teamID, since, offset, limit)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*model.NewTeamMembersList)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 int64
|
||||
if rf, ok := ret.Get(1).(func(string, int64, int, int) int64); ok {
|
||||
r1 = rf(teamID, since, offset, limit)
|
||||
} else {
|
||||
r1 = ret.Get(1).(int64)
|
||||
}
|
||||
|
||||
var r2 error
|
||||
if rf, ok := ret.Get(2).(func(string, int64, int, int) error); ok {
|
||||
r2 = rf(teamID, since, offset, limit)
|
||||
} else {
|
||||
r2 = ret.Error(2)
|
||||
}
|
||||
|
||||
return r0, r1, r2
|
||||
}
|
||||
|
||||
// GetTeamMembersForExport provides a mock function with given fields: userID
|
||||
func (_m *TeamStore) GetTeamMembersForExport(userID string) ([]*model.TeamMemberForExport, error) {
|
||||
ret := _m.Called(userID)
|
||||
|
||||
@@ -73,6 +73,7 @@ func TestTeamStore(t *testing.T, ss store.Store) {
|
||||
t.Run("GetTeamMembersForExport", func(t *testing.T) { testTeamStoreGetTeamMembersForExport(t, ss) })
|
||||
t.Run("GetTeamsForUserWithPagination", func(t *testing.T) { testTeamMembersWithPagination(t, ss) })
|
||||
t.Run("GroupSyncedTeamCount", func(t *testing.T) { testGroupSyncedTeamCount(t, ss) })
|
||||
t.Run("GetNewTeamMembersSince", func(t *testing.T) { testGetNewTeamMembersSince(t, ss) })
|
||||
}
|
||||
|
||||
func testTeamStoreSave(t *testing.T, ss store.Store) {
|
||||
@@ -3618,3 +3619,17 @@ func testGroupSyncedTeamCount(t *testing.T, ss store.Store) {
|
||||
require.NoError(t, err)
|
||||
require.GreaterOrEqual(t, countAfter, count+1)
|
||||
}
|
||||
|
||||
func testGetNewTeamMembersSince(t *testing.T, ss store.Store) {
|
||||
team, err := ss.Team().Save(&model.Team{
|
||||
DisplayName: NewTestId(),
|
||||
Name: NewTestId(),
|
||||
Email: MakeEmail(),
|
||||
Type: model.TeamInvite,
|
||||
GroupConstrained: model.NewBool(true),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
_, _, err = ss.Team().GetNewTeamMembersSince(team.Id, 0, 0, 1000)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
@@ -8072,6 +8072,22 @@ func (s *TimerLayerTeamStore) GetMembersByIds(teamID string, userIds []string, r
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (s *TimerLayerTeamStore) GetNewTeamMembersSince(teamID string, since int64, offset int, limit int) (*model.NewTeamMembersList, int64, error) {
|
||||
start := time.Now()
|
||||
|
||||
result, resultVar1, err := s.TeamStore.GetNewTeamMembersSince(teamID, since, offset, limit)
|
||||
|
||||
elapsed := float64(time.Since(start)) / float64(time.Second)
|
||||
if s.Root.Metrics != nil {
|
||||
success := "false"
|
||||
if err == nil {
|
||||
success = "true"
|
||||
}
|
||||
s.Root.Metrics.ObserveStoreMethodDuration("TeamStore.GetNewTeamMembersSince", success, elapsed)
|
||||
}
|
||||
return result, resultVar1, err
|
||||
}
|
||||
|
||||
func (s *TimerLayerTeamStore) GetTeamMembersForExport(userID string) ([]*model.TeamMemberForExport, error) {
|
||||
start := time.Now()
|
||||
|
||||
|
||||
Ссылка в новой задаче
Block a user