[MM-44307] On Limits Change, archive or restore teams according to the limits (#20247)
Automatic Merge
Этот коммит содержится в:
@@ -536,7 +536,9 @@ func handleCWSWebhook(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
return true
|
||||
}, plugin.OnCloudLimitsUpdatedID)
|
||||
}
|
||||
c.App.AdjustInProductLimits(event.ProductLimits, event.Subscription)
|
||||
}
|
||||
|
||||
if err := c.App.Cloud().UpdateSubscriptionFromHook(event.ProductLimits, event.Subscription); err != nil {
|
||||
c.Err = model.NewAppError("Api4.handleCWSWebhook", "api.cloud.subscription.update_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
|
||||
18
api4/team.go
18
api4/team.go
@@ -43,6 +43,7 @@ func (api *API) InitTeam() {
|
||||
api.BaseRoutes.Team.Handle("", api.APISessionRequired(getTeam)).Methods("GET")
|
||||
api.BaseRoutes.Team.Handle("", api.APISessionRequired(updateTeam)).Methods("PUT")
|
||||
api.BaseRoutes.Team.Handle("", api.APISessionRequired(deleteTeam)).Methods("DELETE")
|
||||
api.BaseRoutes.Team.Handle("/except", api.APISessionRequired(softDeleteTeamsExcept)).Methods("DELETE")
|
||||
api.BaseRoutes.Team.Handle("/patch", api.APISessionRequired(patchTeam)).Methods("PUT")
|
||||
api.BaseRoutes.Team.Handle("/restore", api.APISessionRequired(restoreTeam)).Methods("POST")
|
||||
api.BaseRoutes.Team.Handle("/privacy", api.APISessionRequired(updateTeamPrivacy)).Methods("PUT")
|
||||
@@ -402,6 +403,23 @@ func deleteTeam(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
ReturnStatusOK(w)
|
||||
}
|
||||
|
||||
func softDeleteTeamsExcept(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
c.RequireTeamId()
|
||||
if c.Err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), c.Params.TeamId, model.PermissionManageTeam) {
|
||||
c.SetPermissionError(model.PermissionManageTeam)
|
||||
return
|
||||
}
|
||||
|
||||
err := c.App.SoftDeleteAllTeamsExcept(c.Params.TeamId)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
}
|
||||
}
|
||||
|
||||
func getTeamsForUser(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
c.RequireUserId()
|
||||
if c.Err != nil {
|
||||
|
||||
@@ -387,6 +387,8 @@ type AppIface interface {
|
||||
AddUserToTeamByTeamId(c *request.Context, teamID string, user *model.User) *model.AppError
|
||||
AddUserToTeamByToken(c *request.Context, userID string, tokenID string) (*model.Team, *model.TeamMember, *model.AppError)
|
||||
AdjustImage(file io.Reader) (*bytes.Buffer, *model.AppError)
|
||||
AdjustInProductLimits(limits *model.ProductLimits, subscription *model.Subscription) *model.AppError
|
||||
AdjustTeamsFromProductLimits(teamLimits *model.TeamsLimits) *model.AppError
|
||||
AllowOAuthAppAccessToUser(userID string, authRequest *model.AuthorizeRequest) (string, *model.AppError)
|
||||
AppendFile(fr io.Reader, path string) (int64, *model.AppError)
|
||||
AsymmetricSigningKey() *ecdsa.PrivateKey
|
||||
@@ -1022,6 +1024,7 @@ type AppIface interface {
|
||||
SetTeamIconFromFile(team *model.Team, file io.Reader) *model.AppError
|
||||
SetTeamIconFromMultiPartFile(teamID string, file multipart.File) *model.AppError
|
||||
SlackImport(c *request.Context, fileData multipart.File, fileSize int64, teamID string) (*model.AppError, *bytes.Buffer)
|
||||
SoftDeleteAllTeamsExcept(teamID string) *model.AppError
|
||||
SoftDeleteTeam(teamID string) *model.AppError
|
||||
Srv() *Server
|
||||
SubmitInteractiveDialog(c *request.Context, request model.SubmitDialogRequest) (*model.SubmitDialogResponse, *model.AppError)
|
||||
|
||||
11
app/cloud.go
11
app/cloud.go
@@ -37,6 +37,17 @@ func (a *App) SendPaymentFailedEmail(failedPayment *model.FailedPayment) *model.
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *App) AdjustInProductLimits(limits *model.ProductLimits, subscription *model.Subscription) *model.AppError {
|
||||
if limits.Teams != nil && limits.Teams.Active != nil && *limits.Teams.Active > 0 {
|
||||
err := a.AdjustTeamsFromProductLimits(limits.Teams)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *App) SendUpgradeConfirmationEmail() *model.AppError {
|
||||
sysAdmins, e := a.getSysAdminsEmailRecipients()
|
||||
if e != nil {
|
||||
|
||||
@@ -625,6 +625,50 @@ func (a *OpenTracingAppLayer) AdjustImage(file io.Reader) (*bytes.Buffer, *model
|
||||
return resultVar0, resultVar1
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) AdjustInProductLimits(limits *model.ProductLimits, subscription *model.Subscription) *model.AppError {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.AdjustInProductLimits")
|
||||
|
||||
a.ctx = newCtx
|
||||
a.app.Srv().Store.SetContext(newCtx)
|
||||
defer func() {
|
||||
a.app.Srv().Store.SetContext(origCtx)
|
||||
a.ctx = origCtx
|
||||
}()
|
||||
|
||||
defer span.Finish()
|
||||
resultVar0 := a.app.AdjustInProductLimits(limits, subscription)
|
||||
|
||||
if resultVar0 != nil {
|
||||
span.LogFields(spanlog.Error(resultVar0))
|
||||
ext.Error.Set(span, true)
|
||||
}
|
||||
|
||||
return resultVar0
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) AdjustTeamsFromProductLimits(teamLimits *model.TeamsLimits) *model.AppError {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.AdjustTeamsFromProductLimits")
|
||||
|
||||
a.ctx = newCtx
|
||||
a.app.Srv().Store.SetContext(newCtx)
|
||||
defer func() {
|
||||
a.app.Srv().Store.SetContext(origCtx)
|
||||
a.ctx = origCtx
|
||||
}()
|
||||
|
||||
defer span.Finish()
|
||||
resultVar0 := a.app.AdjustTeamsFromProductLimits(teamLimits)
|
||||
|
||||
if resultVar0 != nil {
|
||||
span.LogFields(spanlog.Error(resultVar0))
|
||||
ext.Error.Set(span, true)
|
||||
}
|
||||
|
||||
return resultVar0
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) AllowOAuthAppAccessToUser(userID string, authRequest *model.AuthorizeRequest) (string, *model.AppError) {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.AllowOAuthAppAccessToUser")
|
||||
@@ -15583,6 +15627,28 @@ func (a *OpenTracingAppLayer) SlackImport(c *request.Context, fileData multipart
|
||||
return resultVar0, resultVar1
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) SoftDeleteAllTeamsExcept(teamID string) *model.AppError {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.SoftDeleteAllTeamsExcept")
|
||||
|
||||
a.ctx = newCtx
|
||||
a.app.Srv().Store.SetContext(newCtx)
|
||||
defer func() {
|
||||
a.app.Srv().Store.SetContext(origCtx)
|
||||
a.ctx = origCtx
|
||||
}()
|
||||
|
||||
defer span.Finish()
|
||||
resultVar0 := a.app.SoftDeleteAllTeamsExcept(teamID)
|
||||
|
||||
if resultVar0 != nil {
|
||||
span.LogFields(spanlog.Error(resultVar0))
|
||||
ext.Error.Set(span, true)
|
||||
}
|
||||
|
||||
return resultVar0
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) SoftDeleteTeam(teamID string) *model.AppError {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.SoftDeleteTeam")
|
||||
|
||||
101
app/team.go
101
app/team.go
@@ -14,6 +14,7 @@ import (
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/app/email"
|
||||
@@ -29,6 +30,106 @@ import (
|
||||
"github.com/mattermost/mattermost-server/v6/store/sqlstore"
|
||||
)
|
||||
|
||||
func (a *App) AdjustTeamsFromProductLimits(teamLimits *model.TeamsLimits) *model.AppError {
|
||||
maxActiveTeams := *teamLimits.Active
|
||||
teams, appErr := a.GetAllTeams()
|
||||
if appErr != nil {
|
||||
return appErr
|
||||
}
|
||||
|
||||
if teams == nil {
|
||||
return nil
|
||||
}
|
||||
// Sort the list of teams based on their creation date
|
||||
sort.Slice(teams, func(i, j int) bool {
|
||||
return teams[i].CreateAt < teams[j].CreateAt
|
||||
})
|
||||
|
||||
var activeTeams []*model.Team
|
||||
var cloudArchivedTeams []*model.Team
|
||||
for _, team := range teams {
|
||||
if team.DeleteAt == 0 {
|
||||
activeTeams = append(activeTeams, team)
|
||||
}
|
||||
if team.DeleteAt > 0 && team.CloudLimitsArchived {
|
||||
cloudArchivedTeams = append(cloudArchivedTeams, team)
|
||||
}
|
||||
}
|
||||
|
||||
if len(activeTeams) > maxActiveTeams {
|
||||
// If there are more active teams than allowed, we must archive them
|
||||
// Remove the first n elements (where n is the allowed number of teams) so they aren't archived
|
||||
|
||||
teamsToArchive := activeTeams[maxActiveTeams:]
|
||||
|
||||
for _, team := range teamsToArchive {
|
||||
cloudLimitsArchived := true
|
||||
// Archive the remainder
|
||||
patch := model.TeamPatch{CloudLimitsArchived: &cloudLimitsArchived}
|
||||
_, err := a.PatchTeam(team.Id, &patch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = a.SoftDeleteTeam(team.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else if len(activeTeams) < maxActiveTeams && len(cloudArchivedTeams) > 0 {
|
||||
// If the number of activeTeams is less than the allowed limit, and there are some cloudArchivedTeams, we can restore these cloudArchivedTeams
|
||||
activeTeamsBeforeLimit := maxActiveTeams - len(activeTeams)
|
||||
teamsToRestore := cloudArchivedTeams
|
||||
// If the number of active teams remaining before the limit is hit is fewer than the number of cloudArchivedTeams, trim the list (still according to CreateAt)
|
||||
// Otherwise, we can restore all of the cloudArchivedTeams without hitting the limit, so don't filter the list
|
||||
if activeTeamsBeforeLimit < len(cloudArchivedTeams) {
|
||||
teamsToRestore = cloudArchivedTeams[:(activeTeamsBeforeLimit)]
|
||||
}
|
||||
|
||||
cloudLimitsArchived := false
|
||||
patch := &model.TeamPatch{CloudLimitsArchived: &cloudLimitsArchived}
|
||||
for _, team := range teamsToRestore {
|
||||
err := a.RestoreTeam(team.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = a.PatchTeam(team.Id, patch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *App) SoftDeleteAllTeamsExcept(teamID string) *model.AppError {
|
||||
teams, appErr := a.GetAllTeams()
|
||||
if appErr != nil {
|
||||
return appErr
|
||||
}
|
||||
|
||||
if teams == nil {
|
||||
return nil
|
||||
}
|
||||
cloudLimitsArchived := true
|
||||
patch := &model.TeamPatch{CloudLimitsArchived: &cloudLimitsArchived}
|
||||
for _, team := range teams {
|
||||
if team.Id != teamID {
|
||||
_, err := a.PatchTeam(team.Id, patch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = a.SoftDeleteTeam(team.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *App) CreateTeam(c *request.Context, team *model.Team) (*model.Team, *model.AppError) {
|
||||
rteam, err := a.ch.srv.teamService.CreateTeam(team)
|
||||
if err != nil {
|
||||
|
||||
191
app/team_test.go
191
app/team_test.go
@@ -466,6 +466,197 @@ func TestAddUserToTeamByTeamId(t *testing.T) {
|
||||
|
||||
}
|
||||
|
||||
func TestSoftDeleteAllTeamsExcept(t *testing.T) {
|
||||
th := Setup(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
teams := []*model.Team{
|
||||
{
|
||||
DisplayName: "team-1",
|
||||
Name: "team-1",
|
||||
Email: "foo@foo.com",
|
||||
Type: model.TeamOpen,
|
||||
},
|
||||
}
|
||||
teamId := ""
|
||||
for _, create := range teams {
|
||||
team, err := th.App.CreateTeam(th.Context, create)
|
||||
require.Nil(t, err)
|
||||
teamId = team.Id
|
||||
}
|
||||
|
||||
err := th.App.SoftDeleteAllTeamsExcept(teamId)
|
||||
assert.Nil(t, err)
|
||||
allTeams, err := th.App.GetAllTeams()
|
||||
require.Nil(t, err)
|
||||
for _, team := range allTeams {
|
||||
if team.Id == teamId {
|
||||
require.Equal(t, int64(0), team.DeleteAt)
|
||||
require.Equal(t, false, team.CloudLimitsArchived)
|
||||
} else {
|
||||
require.NotEqual(t, int64(0), team.DeleteAt)
|
||||
require.Equal(t, true, team.CloudLimitsArchived)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestAdjustTeamsFromProductLimits(t *testing.T) {
|
||||
th := Setup(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
teams := []*model.Team{
|
||||
{
|
||||
DisplayName: "team-1",
|
||||
Name: "team-1",
|
||||
Email: "foo@foo.com",
|
||||
Type: model.TeamOpen,
|
||||
},
|
||||
{
|
||||
DisplayName: "team-2",
|
||||
Name: "team-2",
|
||||
Email: "foo@foo.com",
|
||||
Type: model.TeamOpen,
|
||||
},
|
||||
{
|
||||
DisplayName: "team-3",
|
||||
Name: "team-3",
|
||||
Email: "foo@foo.com",
|
||||
Type: model.TeamOpen,
|
||||
},
|
||||
}
|
||||
teamIds := []string{}
|
||||
for _, create := range teams {
|
||||
team, err := th.App.CreateTeam(th.Context, create)
|
||||
require.Nil(t, err)
|
||||
teamIds = append(teamIds, team.Id)
|
||||
}
|
||||
t.Run("Should soft delete teams if there are more teams than the limit", func(t *testing.T) {
|
||||
activeLimit := 1
|
||||
teamLimits := &model.TeamsLimits{Active: &activeLimit}
|
||||
|
||||
err := th.App.AdjustTeamsFromProductLimits(teamLimits)
|
||||
require.Nil(t, err)
|
||||
|
||||
teamsList, err := th.App.GetTeams(teamIds)
|
||||
|
||||
require.Nil(t, err)
|
||||
|
||||
// Sort the list of teams based on their creation date
|
||||
sort.Slice(teamsList, func(i, j int) bool {
|
||||
return teamsList[i].CreateAt < teamsList[j].CreateAt
|
||||
})
|
||||
|
||||
for i := range teamsList {
|
||||
require.Equal(t, teamsList[i].DisplayName, teams[i].DisplayName)
|
||||
require.NotEqual(t, 0, teamsList[i].DeleteAt)
|
||||
require.Equal(t, true, teamsList[i].CloudLimitsArchived)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Should not do anything if the amount of teams is equal to the limit", func(t *testing.T) {
|
||||
|
||||
expectedTeamsList, err := th.App.GetAllTeams()
|
||||
|
||||
var expectedActiveTeams []*model.Team
|
||||
var expectedCloudArchivedTeams []*model.Team
|
||||
for _, team := range expectedTeamsList {
|
||||
if team.DeleteAt == 0 {
|
||||
expectedActiveTeams = append(expectedActiveTeams, team)
|
||||
}
|
||||
if team.DeleteAt > 0 && team.CloudLimitsArchived {
|
||||
expectedCloudArchivedTeams = append(expectedCloudArchivedTeams, team)
|
||||
}
|
||||
}
|
||||
|
||||
require.Nil(t, err)
|
||||
|
||||
activeLimit := len(expectedActiveTeams)
|
||||
teamLimits := &model.TeamsLimits{Active: &activeLimit}
|
||||
err = th.App.AdjustTeamsFromProductLimits(teamLimits)
|
||||
require.Nil(t, err)
|
||||
|
||||
actualTeamsList, err := th.App.GetAllTeams()
|
||||
|
||||
require.Nil(t, err)
|
||||
var actualActiveTeams []*model.Team
|
||||
var actualCloudArchivedTeams []*model.Team
|
||||
for _, team := range actualTeamsList {
|
||||
if team.DeleteAt == 0 {
|
||||
actualActiveTeams = append(actualActiveTeams, team)
|
||||
}
|
||||
if team.DeleteAt > 0 && team.CloudLimitsArchived {
|
||||
actualCloudArchivedTeams = append(actualCloudArchivedTeams, team)
|
||||
}
|
||||
}
|
||||
|
||||
require.Equal(t, len(expectedActiveTeams), len(actualActiveTeams))
|
||||
require.Equal(t, len(expectedCloudArchivedTeams), len(actualCloudArchivedTeams))
|
||||
})
|
||||
|
||||
t.Run("Should restore archived teams if limit increases", func(t *testing.T) {
|
||||
activeLimit := 1
|
||||
teamLimits := &model.TeamsLimits{Active: &activeLimit}
|
||||
|
||||
err := th.App.AdjustTeamsFromProductLimits(teamLimits)
|
||||
require.Nil(t, err)
|
||||
activeLimit = 10000 // make the limit extremely high so all teams are enabled
|
||||
teamLimits = &model.TeamsLimits{Active: &activeLimit}
|
||||
|
||||
err = th.App.AdjustTeamsFromProductLimits(teamLimits)
|
||||
require.Nil(t, err)
|
||||
|
||||
teamsList, err := th.App.GetTeams(teamIds)
|
||||
|
||||
require.Nil(t, err)
|
||||
|
||||
// Sort the list of teams based on their creation date
|
||||
sort.Slice(teamsList, func(i, j int) bool {
|
||||
return teamsList[i].CreateAt < teamsList[j].CreateAt
|
||||
})
|
||||
|
||||
for i := range teamsList {
|
||||
require.Equal(t, teamsList[i].DisplayName, teams[i].DisplayName)
|
||||
require.Equal(t, int64(0), teamsList[i].DeleteAt)
|
||||
require.Equal(t, false, teamsList[i].CloudLimitsArchived)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Should only restore teams that were archived by cloud limits", func(t *testing.T) {
|
||||
|
||||
activeLimit := 1
|
||||
teamLimits := &model.TeamsLimits{Active: &activeLimit}
|
||||
|
||||
err := th.App.AdjustTeamsFromProductLimits(teamLimits)
|
||||
require.Nil(t, err)
|
||||
|
||||
cloudLimitsArchived := false
|
||||
patch := &model.TeamPatch{CloudLimitsArchived: &cloudLimitsArchived}
|
||||
team, err := th.App.PatchTeam(teamIds[0], patch)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, false, team.CloudLimitsArchived)
|
||||
|
||||
activeLimit = 10000 // make the limit extremely high so all teams are enabled
|
||||
teamLimits = &model.TeamsLimits{Active: &activeLimit}
|
||||
|
||||
err = th.App.AdjustTeamsFromProductLimits(teamLimits)
|
||||
require.Nil(t, err)
|
||||
|
||||
teamsList, err := th.App.GetTeams(teamIds)
|
||||
|
||||
require.Nil(t, err)
|
||||
|
||||
// Sort the list of teams based on their creation date
|
||||
sort.Slice(teamsList, func(i, j int) bool {
|
||||
return teamsList[i].CreateAt < teamsList[j].CreateAt
|
||||
})
|
||||
|
||||
require.NotEqual(t, int64(0), teamsList[0].DeleteAt)
|
||||
require.Equal(t, int64(0), teamsList[1].DeleteAt)
|
||||
require.Equal(t, int64(0), teamsList[2].DeleteAt)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestPermanentDeleteTeam(t *testing.T) {
|
||||
th := Setup(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
14
db/migrations/mysql/000085_add_cloud_limits_archived.down.sql
Обычный файл
14
db/migrations/mysql/000085_add_cloud_limits_archived.down.sql
Обычный файл
@@ -0,0 +1,14 @@
|
||||
SET @preparedStatement = (SELECT IF(
|
||||
EXISTS (
|
||||
SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE table_name = 'Teams'
|
||||
AND table_schema = DATABASE()
|
||||
AND column_name = 'CloudLimitsArchived'
|
||||
),
|
||||
'ALTER TABLE Teams DROP COLUMN CloudLimitsArchived;',
|
||||
'SELECT 1'
|
||||
));
|
||||
|
||||
PREPARE alterIfExists FROM @preparedStatement;
|
||||
EXECUTE alterIfExists;
|
||||
DEALLOCATE PREPARE alterIfExists;
|
||||
14
db/migrations/mysql/000085_add_cloud_limits_archived.up.sql
Обычный файл
14
db/migrations/mysql/000085_add_cloud_limits_archived.up.sql
Обычный файл
@@ -0,0 +1,14 @@
|
||||
SET @preparedStatement = (SELECT IF(
|
||||
NOT EXISTS(
|
||||
SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE table_name = 'Teams'
|
||||
AND table_schema = DATABASE()
|
||||
AND column_name = 'CloudLimitsArchived'
|
||||
),
|
||||
'ALTER TABLE Teams ADD COLUMN CloudLimitsArchived BOOLEAN NOT NULL DEFAULT FALSE;',
|
||||
'SELECT 1'
|
||||
));
|
||||
|
||||
PREPARE alterIfNotExists FROM @preparedStatement;
|
||||
EXECUTE alterIfNotExists;
|
||||
DEALLOCATE PREPARE alterIfNotExists;
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE teams DROP COLUMN IF EXISTS CloudLimitsArchived;
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE teams ADD COLUMN IF NOT EXISTS CloudLimitsArchived bool NOT NULL DEFAULT FALSE;
|
||||
@@ -24,32 +24,34 @@ const (
|
||||
)
|
||||
|
||||
type Team struct {
|
||||
Id string `json:"id"`
|
||||
CreateAt int64 `json:"create_at"`
|
||||
UpdateAt int64 `json:"update_at"`
|
||||
DeleteAt int64 `json:"delete_at"`
|
||||
DisplayName string `json:"display_name"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Email string `json:"email"`
|
||||
Type string `json:"type"`
|
||||
CompanyName string `json:"company_name"`
|
||||
AllowedDomains string `json:"allowed_domains"`
|
||||
InviteId string `json:"invite_id"`
|
||||
AllowOpenInvite bool `json:"allow_open_invite"`
|
||||
LastTeamIconUpdate int64 `json:"last_team_icon_update,omitempty"`
|
||||
SchemeId *string `json:"scheme_id"`
|
||||
GroupConstrained *bool `json:"group_constrained"`
|
||||
PolicyID *string `json:"policy_id"`
|
||||
Id string `json:"id"`
|
||||
CreateAt int64 `json:"create_at"`
|
||||
UpdateAt int64 `json:"update_at"`
|
||||
DeleteAt int64 `json:"delete_at"`
|
||||
DisplayName string `json:"display_name"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Email string `json:"email"`
|
||||
Type string `json:"type"`
|
||||
CompanyName string `json:"company_name"`
|
||||
AllowedDomains string `json:"allowed_domains"`
|
||||
InviteId string `json:"invite_id"`
|
||||
AllowOpenInvite bool `json:"allow_open_invite"`
|
||||
LastTeamIconUpdate int64 `json:"last_team_icon_update,omitempty"`
|
||||
SchemeId *string `json:"scheme_id"`
|
||||
GroupConstrained *bool `json:"group_constrained"`
|
||||
PolicyID *string `json:"policy_id"`
|
||||
CloudLimitsArchived bool `json:"cloud_limits_archived"`
|
||||
}
|
||||
|
||||
type TeamPatch struct {
|
||||
DisplayName *string `json:"display_name"`
|
||||
Description *string `json:"description"`
|
||||
CompanyName *string `json:"company_name"`
|
||||
AllowedDomains *string `json:"allowed_domains"`
|
||||
AllowOpenInvite *bool `json:"allow_open_invite"`
|
||||
GroupConstrained *bool `json:"group_constrained"`
|
||||
DisplayName *string `json:"display_name"`
|
||||
Description *string `json:"description"`
|
||||
CompanyName *string `json:"company_name"`
|
||||
AllowedDomains *string `json:"allowed_domains"`
|
||||
AllowOpenInvite *bool `json:"allow_open_invite"`
|
||||
GroupConstrained *bool `json:"group_constrained"`
|
||||
CloudLimitsArchived *bool `json:"cloud_limits_archived"`
|
||||
}
|
||||
|
||||
type TeamForExport struct {
|
||||
@@ -246,6 +248,10 @@ func (o *Team) Patch(patch *TeamPatch) {
|
||||
if patch.GroupConstrained != nil {
|
||||
o.GroupConstrained = patch.GroupConstrained
|
||||
}
|
||||
|
||||
if patch.CloudLimitsArchived != nil {
|
||||
o.CloudLimitsArchived = *patch.CloudLimitsArchived
|
||||
}
|
||||
}
|
||||
|
||||
func (o *Team) IsGroupConstrained() bool {
|
||||
|
||||
@@ -227,10 +227,10 @@ func (s SqlTeamStore) Save(team *model.Team) (*model.Team, error) {
|
||||
|
||||
if _, err := s.GetMasterX().NamedExec(`INSERT INTO Teams
|
||||
(Id, CreateAt, UpdateAt, DeleteAt, DisplayName, Name, Description, Email, Type, CompanyName, AllowedDomains,
|
||||
InviteId, AllowOpenInvite, LastTeamIconUpdate, SchemeId, GroupConstrained)
|
||||
InviteId, AllowOpenInvite, LastTeamIconUpdate, SchemeId, GroupConstrained, CloudLimitsArchived)
|
||||
VALUES
|
||||
(:Id, :CreateAt, :UpdateAt, :DeleteAt, :DisplayName, :Name, :Description, :Email, :Type, :CompanyName, :AllowedDomains,
|
||||
:InviteId, :AllowOpenInvite, :LastTeamIconUpdate, :SchemeId, :GroupConstrained)`, team); err != nil {
|
||||
:InviteId, :AllowOpenInvite, :LastTeamIconUpdate, :SchemeId, :GroupConstrained, :CloudLimitsArchived)`, team); err != nil {
|
||||
if IsUniqueConstraintError(err, []string{"Name", "teams_name_key"}) {
|
||||
return nil, store.NewErrInvalidInput("Team", "id", team.Id)
|
||||
}
|
||||
@@ -268,7 +268,7 @@ func (s SqlTeamStore) Update(team *model.Team) (*model.Team, error) {
|
||||
SET CreateAt=:CreateAt, UpdateAt=:UpdateAt, DeleteAt=:DeleteAt, DisplayName=:DisplayName, Name=:Name,
|
||||
Description=:Description, Email=:Email, Type=:Type, CompanyName=:CompanyName, AllowedDomains=:AllowedDomains,
|
||||
InviteId=:InviteId, AllowOpenInvite=:AllowOpenInvite, LastTeamIconUpdate=:LastTeamIconUpdate,
|
||||
SchemeId=:SchemeId, GroupConstrained=:GroupConstrained
|
||||
SchemeId=:SchemeId, GroupConstrained=:GroupConstrained, CloudLimitsArchived=:CloudLimitsArchived
|
||||
WHERE Id=:Id`, team)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to update Team with id=%s", team.Id)
|
||||
|
||||
Ссылка в новой задаче
Block a user