Skip to content

Commit

Permalink
Added post limit warning (#26793)
Browse files Browse the repository at this point in the history
* Renamed user limit API to app limit API

* Added post warning limit

* Added tests

* Fixed types

* Renamed AppLimits to ServerLimits

* Fixed tests and review fixes

* Updated generated code

* Updated server i18n

* Fixed TestCreateUserOrGuest test

* Exclude deleted posts from post count for liims

* Reduced limits for ease of testing

* Restored original limts
  • Loading branch information
harshilsharma63 committed Apr 18, 2024
1 parent 4571c6e commit b4a1b33
Show file tree
Hide file tree
Showing 31 changed files with 448 additions and 153 deletions.
2 changes: 1 addition & 1 deletion api/v4/source/definitions.yaml
Expand Up @@ -3637,7 +3637,7 @@ components:
state:
description: The current state of the installation
type: string
UserLimits:
ServerLimits:
type: object
properties:
maxUsersLimit:
Expand Down
16 changes: 8 additions & 8 deletions api/v4/source/limits.yaml
@@ -1,25 +1,25 @@
/api/v4/limits/users:
/api/v4/limits/server:
get:
tags:
- users
summary: Gets the user limits for the server
summary: Gets the server limits for the server
description: >
Gets the user limits for the server
Gets the server limits for the server
##### Permissions
Requires `sysconsole_read_user_management_users`.
operationId: getUserLimits
operationId: GetServerLimits
responses:
"200":
description: User limits for server
description: App limits for server
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/UserLimits"
$ref: "#/components/schemas/ServerLimits"
"400":
$ref: "#/components/responses/BadRequest"
"401":
Expand Down
10 changes: 5 additions & 5 deletions server/channels/api4/limits.go
Expand Up @@ -13,22 +13,22 @@ import (
)

func (api *API) InitLimits() {
api.BaseRoutes.Limits.Handle("/users", api.APISessionRequired(getUserLimits)).Methods("GET")
api.BaseRoutes.Limits.Handle("/server", api.APISessionRequired(getServerLimits)).Methods("GET")
}

func getUserLimits(c *Context, w http.ResponseWriter, r *http.Request) {
func getServerLimits(c *Context, w http.ResponseWriter, r *http.Request) {
if !(c.IsSystemAdmin() && c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PermissionSysconsoleReadUserManagementUsers)) {
c.SetPermissionError(model.PermissionSysconsoleReadUserManagementUsers)
return
}

userLimits, err := c.App.GetUserLimits()
serverLimits, err := c.App.GetServerLimits()
if err != nil {
c.Err = err
return
}

if err := json.NewEncoder(w).Encode(userLimits); err != nil {
c.Logger.Error("Error writing user limits response", mlog.Err(err))
if err := json.NewEncoder(w).Encode(serverLimits); err != nil {
c.Logger.Error("Error writing server limits response", mlog.Err(err))
}
}
2 changes: 1 addition & 1 deletion server/channels/app/app_iface.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

51 changes: 36 additions & 15 deletions server/channels/app/limits.go
Expand Up @@ -12,26 +12,39 @@ import (
)

const (
maxUsersLimit = 10000
maxUsersHardLimit = 11000
maxUsersLimit = 10_000
maxUsersHardLimit = 11_000

maxPostLimit = 5_000_000
)

func (a *App) GetUserLimits() (*model.UserLimits, *model.AppError) {
if !a.shouldShowUserLimits() {
return &model.UserLimits{}, nil
func (a *App) GetServerLimits() (*model.ServerLimits, *model.AppError) {
var limits = &model.ServerLimits{}

if a.shouldShowUserLimits() {
activeUserCount, appErr := a.Srv().Store().User().Count(model.UserCountOptions{})
if appErr != nil {
mlog.Error("Failed to get active user count from database", mlog.String("error", appErr.Error()))
return nil, model.NewAppError("GetServerLimits", "app.limits.get_app_limits.user_count.store_error", nil, "", http.StatusInternalServerError).Wrap(appErr)
}

limits.ActiveUserCount = activeUserCount
limits.MaxUsersLimit = maxUsersLimit
limits.MaxUsersHardLimit = maxUsersHardLimit
}

activeUserCount, appErr := a.Srv().Store().User().Count(model.UserCountOptions{})
if appErr != nil {
mlog.Error("Failed to get active user count from database", mlog.String("error", appErr.Error()))
return nil, model.NewAppError("GetUsersLimits", "app.limits.get_user_limits.user_count.store_error", nil, "", http.StatusInternalServerError).Wrap(appErr)
if a.shouldShowPostLimits() {
postCount, appErr := a.Srv().Store().Post().AnalyticsPostCount(&model.PostCountOptions{ExcludeDeleted: true})
if appErr != nil {
mlog.Error("Failed to get post count from database", mlog.String("error", appErr.Error()))
return nil, model.NewAppError("GetServerLimits", "app.limits.get_server_limits.post_count.store_error", nil, "", http.StatusInternalServerError).Wrap(appErr)
}

limits.MaxPostLimit = maxPostLimit
limits.PostCount = postCount
}

return &model.UserLimits{
ActiveUserCount: activeUserCount,
MaxUsersLimit: maxUsersLimit,
MaxUsersHardLimit: maxUsersHardLimit,
}, nil
return limits, nil
}

func (a *App) shouldShowUserLimits() bool {
Expand All @@ -42,8 +55,16 @@ func (a *App) shouldShowUserLimits() bool {
return a.License() == nil
}

func (a *App) shouldShowPostLimits() bool {
if maxPostLimit == 0 {
return false
}

return a.License() == nil
}

func (a *App) isHardUserLimitExceeded() (bool, *model.AppError) {
userLimits, appErr := a.GetUserLimits()
userLimits, appErr := a.GetServerLimits()
if appErr != nil {
return false, appErr
}
Expand Down
130 changes: 90 additions & 40 deletions server/channels/app/limits_test.go
Expand Up @@ -6,127 +6,177 @@ package app
import (
"testing"

"github.com/mattermost/mattermost/server/public/shared/request"

"github.com/mattermost/mattermost/server/public/model"
"github.com/stretchr/testify/require"
)

func TestGetUserLimits(t *testing.T) {
th := Setup(t).InitBasic()
defer th.TearDown()

func TestGetServerLimits(t *testing.T) {
t.Run("base case", func(t *testing.T) {
userLimits, appErr := th.App.GetUserLimits()
th := Setup(t).InitBasic()
defer th.TearDown()

serverLimits, appErr := th.App.GetServerLimits()
require.Nil(t, appErr)

// InitBasic creates 3 users by default
require.Equal(t, int64(3), userLimits.ActiveUserCount)
require.Equal(t, int64(10000), userLimits.MaxUsersLimit)
require.Equal(t, int64(3), serverLimits.ActiveUserCount)
require.Equal(t, int64(10000), serverLimits.MaxUsersLimit)

// 5 posts are created by default
require.Equal(t, int64(5), serverLimits.PostCount)
require.Equal(t, int64(5_000_000), serverLimits.MaxPostLimit)
})

t.Run("user count should increase on creating new user and decrease on permanently deleting", func(t *testing.T) {
userLimits, appErr := th.App.GetUserLimits()
th := Setup(t).InitBasic()
defer th.TearDown()

serverLimits, appErr := th.App.GetServerLimits()
require.Nil(t, appErr)
require.Equal(t, int64(3), userLimits.ActiveUserCount)
require.Equal(t, int64(3), serverLimits.ActiveUserCount)

// now we create a new user
newUser := th.CreateUser()

userLimits, appErr = th.App.GetUserLimits()
serverLimits, appErr = th.App.GetServerLimits()
require.Nil(t, appErr)
require.Equal(t, int64(4), userLimits.ActiveUserCount)
require.Equal(t, int64(4), serverLimits.ActiveUserCount)

// now we'll delete the user
_ = th.App.PermanentDeleteUser(th.Context, newUser)
userLimits, appErr = th.App.GetUserLimits()
serverLimits, appErr = th.App.GetServerLimits()
require.Nil(t, appErr)
require.Equal(t, int64(3), userLimits.ActiveUserCount)
require.Equal(t, int64(3), serverLimits.ActiveUserCount)
})

t.Run("user count should increase on creating new guest user and decrease on permanently deleting", func(t *testing.T) {
userLimits, appErr := th.App.GetUserLimits()
th := Setup(t).InitBasic()
defer th.TearDown()

serverLimits, appErr := th.App.GetServerLimits()
require.Nil(t, appErr)
require.Equal(t, int64(3), userLimits.ActiveUserCount)
require.Equal(t, int64(3), serverLimits.ActiveUserCount)

// now we create a new user
newGuestUser := th.CreateGuest()

userLimits, appErr = th.App.GetUserLimits()
serverLimits, appErr = th.App.GetServerLimits()
require.Nil(t, appErr)
require.Equal(t, int64(4), userLimits.ActiveUserCount)
require.Equal(t, int64(4), serverLimits.ActiveUserCount)

// now we'll delete the user
_ = th.App.PermanentDeleteUser(th.Context, newGuestUser)
userLimits, appErr = th.App.GetUserLimits()
serverLimits, appErr = th.App.GetServerLimits()
require.Nil(t, appErr)
require.Equal(t, int64(3), userLimits.ActiveUserCount)
require.Equal(t, int64(3), serverLimits.ActiveUserCount)
})

t.Run("user count should increase on creating new user and decrease on soft deleting", func(t *testing.T) {
userLimits, appErr := th.App.GetUserLimits()
th := Setup(t).InitBasic()
defer th.TearDown()

serverLimits, appErr := th.App.GetServerLimits()
require.Nil(t, appErr)
require.Equal(t, int64(3), userLimits.ActiveUserCount)
require.Equal(t, int64(3), serverLimits.ActiveUserCount)

// now we create a new user
newUser := th.CreateUser()

userLimits, appErr = th.App.GetUserLimits()
serverLimits, appErr = th.App.GetServerLimits()
require.Nil(t, appErr)
require.Equal(t, int64(4), userLimits.ActiveUserCount)
require.Equal(t, int64(4), serverLimits.ActiveUserCount)

// now we'll delete the user
_, appErr = th.App.UpdateActive(th.Context, newUser, false)
require.Nil(t, appErr)
userLimits, appErr = th.App.GetUserLimits()
serverLimits, appErr = th.App.GetServerLimits()
require.Nil(t, appErr)
require.Equal(t, int64(3), userLimits.ActiveUserCount)
require.Equal(t, int64(3), serverLimits.ActiveUserCount)
})

t.Run("user count should increase on creating new guest user and decrease on soft deleting", func(t *testing.T) {
userLimits, appErr := th.App.GetUserLimits()
th := Setup(t).InitBasic()
defer th.TearDown()

serverLimits, appErr := th.App.GetServerLimits()
require.Nil(t, appErr)
require.Equal(t, int64(3), userLimits.ActiveUserCount)
require.Equal(t, int64(3), serverLimits.ActiveUserCount)

// now we create a new user
newGuestUser := th.CreateGuest()

userLimits, appErr = th.App.GetUserLimits()
serverLimits, appErr = th.App.GetServerLimits()
require.Nil(t, appErr)
require.Equal(t, int64(4), userLimits.ActiveUserCount)
require.Equal(t, int64(4), serverLimits.ActiveUserCount)

// now we'll delete the user
_, appErr = th.App.UpdateActive(th.Context, newGuestUser, false)
require.Nil(t, appErr)
userLimits, appErr = th.App.GetUserLimits()
serverLimits, appErr = th.App.GetServerLimits()
require.Nil(t, appErr)
require.Equal(t, int64(3), userLimits.ActiveUserCount)
require.Equal(t, int64(3), serverLimits.ActiveUserCount)
})

t.Run("user count should not change on creating or deleting bots", func(t *testing.T) {
userLimits, appErr := th.App.GetUserLimits()
th := Setup(t).InitBasic()
defer th.TearDown()

serverLimits, appErr := th.App.GetServerLimits()
require.Nil(t, appErr)
require.Equal(t, int64(3), userLimits.ActiveUserCount)
require.Equal(t, int64(3), serverLimits.ActiveUserCount)

// now we create a new bot
newBot := th.CreateBot()

userLimits, appErr = th.App.GetUserLimits()
serverLimits, appErr = th.App.GetServerLimits()
require.Nil(t, appErr)
require.Equal(t, int64(3), userLimits.ActiveUserCount)
require.Equal(t, int64(3), serverLimits.ActiveUserCount)

// now we'll delete the bot
_ = th.App.PermanentDeleteBot(th.Context, newBot.UserId)
userLimits, appErr = th.App.GetUserLimits()
serverLimits, appErr = th.App.GetServerLimits()
require.Nil(t, appErr)
require.Equal(t, int64(3), userLimits.ActiveUserCount)
require.Equal(t, int64(3), serverLimits.ActiveUserCount)
})

t.Run("limits should be empty when there is a license", func(t *testing.T) {
th := Setup(t).InitBasic()
defer th.TearDown()

th.App.Srv().SetLicense(model.NewTestLicense())

userLimits, appErr := th.App.GetUserLimits()
serverLimits, appErr := th.App.GetServerLimits()
require.Nil(t, appErr)

require.Equal(t, int64(0), userLimits.ActiveUserCount)
require.Equal(t, int64(0), userLimits.MaxUsersLimit)
require.Equal(t, int64(0), serverLimits.ActiveUserCount)
require.Equal(t, int64(0), serverLimits.MaxUsersLimit)
})

t.Run("post count should increase on creating new post and should decrease on deleting post", func(t *testing.T) {
th := Setup(t).InitBasic()
defer th.TearDown()

serverLimits, appErr := th.App.GetServerLimits()
require.Nil(t, appErr)
require.Equal(t, int64(5), serverLimits.PostCount)

// now we create a new post
team := th.CreateTeam()
channel := th.CreateChannel(request.TestContext(t), team)
post := th.CreatePost(channel)

serverLimits, appErr = th.App.GetServerLimits()
require.Nil(t, appErr)
require.Equal(t, int64(6), serverLimits.PostCount)

// now we'll delete the post
_, appErr = th.App.DeletePost(request.TestContext(t), post.Id, "")
require.Nil(t, appErr)

serverLimits, appErr = th.App.GetServerLimits()
require.Nil(t, appErr)
require.Equal(t, int64(5), serverLimits.PostCount)
})
}

0 comments on commit b4a1b33

Please sign in to comment.