Adds logical deletes to shared channel remotes and remote clusters (#28159)
* Adds logical deletes to shared channel remotes and remote clusters Instead of physically deleting the shared channel remote and remote clusters records when a channel is unshared, a remote uninvited or a remote cluster is deleted, now those have a logical `DeleteAt` field that is set. This allows us to safely restore shared channels between two remote clusters (as of now resetting the cursor without backfilling their contents) and to know which connections were established in the past and now are severed. * Delete the index in remoteclusters before adding the new column * Fix bad error check
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
19733eef1e
Коммит
f8202309ce
@@ -325,6 +325,7 @@ func getRemoteClusters(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
PluginID: c.Params.PluginId,
|
||||
OnlyPlugins: c.Params.OnlyPlugins,
|
||||
ExcludePlugins: c.Params.ExcludePlugins,
|
||||
IncludeDeleted: c.Params.IncludeDeleted,
|
||||
}
|
||||
|
||||
rcs, appErr := c.App.GetAllRemoteClusters(c.Params.Page, c.Params.PerPage, filter)
|
||||
|
||||
@@ -5,7 +5,6 @@ package api4
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/base64"
|
||||
"testing"
|
||||
|
||||
@@ -48,6 +47,13 @@ func TestGetRemoteClusters(t *testing.T) {
|
||||
CreatorId: th.SystemAdminUser.Id,
|
||||
PluginID: model.NewId(),
|
||||
},
|
||||
{
|
||||
RemoteId: model.NewId(),
|
||||
Name: "remote4",
|
||||
SiteURL: "http://example4.com",
|
||||
CreatorId: th.SystemAdminUser.Id,
|
||||
DeleteAt: 123,
|
||||
},
|
||||
}
|
||||
|
||||
for _, rc := range newRCs {
|
||||
@@ -94,6 +100,16 @@ func TestGetRemoteClusters(t *testing.T) {
|
||||
ExpectedError: false,
|
||||
ExpectedNames: []string{"remote1", "remote2", "remote3"},
|
||||
},
|
||||
{
|
||||
Name: "Should return all remote clusters including deleted",
|
||||
Client: th.SystemAdminClient,
|
||||
Page: 0,
|
||||
PerPage: 999999,
|
||||
Filter: model.RemoteClusterQueryFilter{IncludeDeleted: true},
|
||||
ExpectedStatusCode: 200,
|
||||
ExpectedError: false,
|
||||
ExpectedNames: []string{"remote1", "remote2", "remote3", "remote4"},
|
||||
},
|
||||
{
|
||||
Name: "Should return all remote clusters but those belonging to plugins",
|
||||
Client: th.SystemAdminClient,
|
||||
@@ -104,6 +120,16 @@ func TestGetRemoteClusters(t *testing.T) {
|
||||
ExpectedError: false,
|
||||
ExpectedNames: []string{"remote1", "remote2"},
|
||||
},
|
||||
{
|
||||
Name: "Should return all remote clusters but those belonging to plugins, including deleted",
|
||||
Client: th.SystemAdminClient,
|
||||
Page: 0,
|
||||
PerPage: 999999,
|
||||
Filter: model.RemoteClusterQueryFilter{ExcludePlugins: true, IncludeDeleted: true},
|
||||
ExpectedStatusCode: 200,
|
||||
ExpectedError: false,
|
||||
ExpectedNames: []string{"remote1", "remote2", "remote4"},
|
||||
},
|
||||
{
|
||||
Name: "Should return only remote clusters belonging to plugins",
|
||||
Client: th.SystemAdminClient,
|
||||
@@ -572,18 +598,19 @@ func TestDeleteRemoteCluster(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("should correctly delete the remote cluster", func(t *testing.T) {
|
||||
// ensure the remote cluster is not deleted
|
||||
initialRC, appErr := th.App.GetRemoteCluster(rc.RemoteId)
|
||||
require.Nil(t, appErr)
|
||||
require.NotEmpty(t, initialRC)
|
||||
require.Zero(t, initialRC.DeleteAt)
|
||||
|
||||
resp, err := th.SystemAdminClient.DeleteRemoteCluster(context.Background(), rc.RemoteId)
|
||||
CheckNoContentStatus(t, resp)
|
||||
require.NoError(t, err)
|
||||
|
||||
deletedRC, err := th.App.GetRemoteCluster(rc.RemoteId)
|
||||
require.ErrorIs(t, err, sql.ErrNoRows)
|
||||
require.Empty(t, deletedRC)
|
||||
})
|
||||
|
||||
t.Run("should return not found if the remote cluster is already deleted", func(t *testing.T) {
|
||||
resp, err := th.SystemAdminClient.DeleteRemoteCluster(context.Background(), rc.RemoteId)
|
||||
CheckNotFoundStatus(t, resp)
|
||||
require.Error(t, err)
|
||||
deletedRC, appErr := th.App.GetRemoteCluster(rc.RemoteId)
|
||||
require.Nil(t, appErr)
|
||||
require.NotEmpty(t, deletedRC)
|
||||
require.NotZero(t, deletedRC.DeleteAt)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -116,9 +116,10 @@ func getSharedChannelRemotesByRemoteCluster(c *Context, w http.ResponseWriter, r
|
||||
}
|
||||
|
||||
filter := model.SharedChannelRemoteFilterOpts{
|
||||
RemoteId: c.Params.RemoteId,
|
||||
ExcludeHome: c.Params.ExcludeHome,
|
||||
ExcludeRemote: c.Params.ExcludeRemote,
|
||||
RemoteId: c.Params.RemoteId,
|
||||
ExcludeHome: c.Params.ExcludeHome,
|
||||
ExcludeRemote: c.Params.ExcludeRemote,
|
||||
IncludeDeleted: c.Params.IncludeDeleted,
|
||||
}
|
||||
sharedChannelRemotes, err := c.App.GetSharedChannelRemotes(c.Params.Page, c.Params.PerPage, filter)
|
||||
if err != nil {
|
||||
@@ -153,7 +154,7 @@ func inviteRemoteClusterToChannel(c *Context, w http.ResponseWriter, r *http.Req
|
||||
return
|
||||
}
|
||||
|
||||
if _, appErr := c.App.GetRemoteCluster(c.Params.RemoteId); appErr != nil {
|
||||
if rc, appErr := c.App.GetRemoteCluster(c.Params.RemoteId); appErr != nil || rc.DeleteAt != 0 {
|
||||
c.SetInvalidRemoteIdError(c.Params.RemoteId)
|
||||
return
|
||||
}
|
||||
@@ -200,7 +201,7 @@ func uninviteRemoteClusterToChannel(c *Context, w http.ResponseWriter, r *http.R
|
||||
return
|
||||
}
|
||||
|
||||
if _, appErr := c.App.GetRemoteCluster(c.Params.RemoteId); appErr != nil {
|
||||
if rc, appErr := c.App.GetRemoteCluster(c.Params.RemoteId); appErr != nil || rc.DeleteAt != 0 {
|
||||
c.SetInvalidRemoteIdError(c.Params.RemoteId)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -299,6 +299,20 @@ func TestGetSharedChannelRemotesByRemoteCluster(t *testing.T) {
|
||||
_, err = th.App.ShareChannel(th.Context, sc3)
|
||||
require.NoError(t, err)
|
||||
|
||||
c4 := th.CreateChannelWithClientAndTeam(th.Client, model.ChannelTypeOpen, th.BasicTeam.Id)
|
||||
sc4 := &model.SharedChannel{
|
||||
ChannelId: c4.Id,
|
||||
TeamId: th.BasicTeam.Id,
|
||||
ShareName: "shared_4",
|
||||
ShareDisplayName: "Shared Channel 4",
|
||||
CreatorId: th.BasicUser.Id,
|
||||
RemoteId: rc1.RemoteId,
|
||||
Home: false,
|
||||
}
|
||||
|
||||
_, err = th.App.ShareChannel(th.Context, sc4)
|
||||
require.NoError(t, err)
|
||||
|
||||
// for the pagination test, we need to get the channelId of the
|
||||
// second SharedChannelRemote that belongs to RC1, sorted by ID,
|
||||
// so we accumulate those SharedChannelRemotes on creation and
|
||||
@@ -307,7 +321,7 @@ func TestGetSharedChannelRemotesByRemoteCluster(t *testing.T) {
|
||||
sharedChannelRemotesFromRC1 := []*model.SharedChannelRemote{}
|
||||
|
||||
// create the shared channel remotes
|
||||
for _, sc := range []*model.SharedChannel{sc1, sc2, sc3} {
|
||||
for _, sc := range []*model.SharedChannel{sc1, sc2, sc3, sc4} {
|
||||
scr := &model.SharedChannelRemote{
|
||||
Id: model.NewId(),
|
||||
ChannelId: sc.ChannelId,
|
||||
@@ -324,6 +338,14 @@ func TestGetSharedChannelRemotesByRemoteCluster(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// we delete the shared channel remote for sc4
|
||||
scr4, err := th.App.GetSharedChannelRemoteByIds(sc4.ChannelId, sc4.RemoteId)
|
||||
require.NoError(t, err)
|
||||
|
||||
deleted, err := th.App.DeleteSharedChannelRemote(scr4.Id)
|
||||
require.NoError(t, err)
|
||||
require.True(t, deleted)
|
||||
|
||||
sort.Slice(sharedChannelRemotesFromRC1, func(i, j int) bool {
|
||||
return sharedChannelRemotesFromRC1[i].Id < sharedChannelRemotesFromRC1[j].Id
|
||||
})
|
||||
@@ -335,6 +357,7 @@ func TestGetSharedChannelRemotesByRemoteCluster(t *testing.T) {
|
||||
RemoteId string
|
||||
ExcludeHome bool
|
||||
ExcludeRemote bool
|
||||
IncludeDeleted bool
|
||||
Page int
|
||||
PerPage int
|
||||
ExpectedStatusCode int
|
||||
@@ -369,6 +392,17 @@ func TestGetSharedChannelRemotesByRemoteCluster(t *testing.T) {
|
||||
ExpectedError: false,
|
||||
ExpectedIds: []string{sc1.ChannelId, sc2.ChannelId},
|
||||
},
|
||||
{
|
||||
Name: "should return the complete list of shared channel remotes for a remote cluster, including deleted",
|
||||
Client: th.SystemAdminClient,
|
||||
RemoteId: rc1.RemoteId,
|
||||
IncludeDeleted: true,
|
||||
Page: 0,
|
||||
PerPage: 100,
|
||||
ExpectedStatusCode: http.StatusOK,
|
||||
ExpectedError: false,
|
||||
ExpectedIds: []string{sc1.ChannelId, sc2.ChannelId, sc4.ChannelId},
|
||||
},
|
||||
{
|
||||
Name: "should return only the shared channel remotes homed localy",
|
||||
Client: th.SystemAdminClient,
|
||||
@@ -395,6 +429,7 @@ func TestGetSharedChannelRemotesByRemoteCluster(t *testing.T) {
|
||||
Name: "should correctly paginate the results",
|
||||
Client: th.SystemAdminClient,
|
||||
RemoteId: rc1.RemoteId,
|
||||
IncludeDeleted: true,
|
||||
Page: 1,
|
||||
PerPage: 1,
|
||||
ExpectedStatusCode: http.StatusOK,
|
||||
@@ -405,7 +440,7 @@ func TestGetSharedChannelRemotesByRemoteCluster(t *testing.T) {
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.Name, func(t *testing.T) {
|
||||
scrs, resp, err := tc.Client.GetSharedChannelRemotesByRemoteCluster(context.Background(), tc.RemoteId, tc.ExcludeHome, tc.ExcludeRemote, tc.Page, tc.PerPage)
|
||||
scrs, resp, err := tc.Client.GetSharedChannelRemotesByRemoteCluster(context.Background(), tc.RemoteId, tc.ExcludeHome, tc.ExcludeRemote, tc.IncludeDeleted, tc.Page, tc.PerPage)
|
||||
checkHTTPStatus(t, resp, tc.ExpectedStatusCode)
|
||||
if tc.ExpectedError {
|
||||
require.Error(t, err)
|
||||
|
||||
Ссылка в новой задаче
Block a user