MM-16809: Added EnsureChannel Helper (#11852)

This PR adds EnsureChannel helper analogous to EnsureBot helper which creates a new channel if not exists and update the meta data of the channel if exists. Also, it will throw error
if the type of existing channel do not match the type of new channel.
Этот коммит содержится в:
Ankit R. Gadiya
2019-09-04 01:48:08 +05:30
коммит произвёл Ben Schumacher
родитель 8afbe9c6a4
Коммит 5aa24aedc8
3 изменённых файлов: 409 добавлений и 0 удалений

Просмотреть файл

@@ -10,6 +10,7 @@ import (
const (
INTERNAL_KEY_PREFIX = "mmi_"
BOT_USER_KEY = INTERNAL_KEY_PREFIX + "botid"
CHANNEL_KEY = INTERNAL_KEY_PREFIX + "channelid"
)
// Starts the serving of a Mattermost plugin over net/rpc. gRPC is not yet supported.

111
plugin/helpers_channels.go Обычный файл
Просмотреть файл

@@ -0,0 +1,111 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package plugin
import (
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/utils"
"github.com/pkg/errors"
)
func (p *HelpersImpl) EnsureChannel(channel *model.Channel) (retChannelId string, retErr error) {
// Must provide a channel with a name and teadId
if channel == nil || len(channel.Name) < 1 || len(channel.TeamId) < 1 {
return "", errors.New("passed a bad channel, nil or no name or no team id")
}
// If we fail for any reason, this could be a race between creation of channel and
// retrieval from another EnsureChannel. Just try the basic retrieve existing again.
defer func() {
if retChannelId == "" || retErr != nil {
var err error
var channelIdBytes []byte
err = utils.ProgressiveRetry(func() error {
channelIdBytes, err = p.API.KVGet(CHANNEL_KEY)
if err != nil {
return err
}
return nil
})
if err == nil && channelIdBytes != nil {
retChannelId = string(channelIdBytes)
retErr = nil
}
}
}()
// Fetch channel ID from key value store
channelIdBytes, kvGetErr := p.API.KVGet(CHANNEL_KEY)
if kvGetErr != nil {
// Failed to retrive the value of channel
return "", errors.Wrap(kvGetErr, "failed to get channel ID")
}
var existingChannel *model.Channel
var channelGetErr *model.AppError
// If channel ID exists, get existing channel by ID else get it by Name
if channelIdBytes != nil {
existingChannel, channelGetErr = p.API.GetChannel(string(channelIdBytes))
if channelGetErr != nil {
return "", errors.Wrap(channelGetErr, "failed to get channel by ID")
}
} else {
existingChannel, channelGetErr = p.API.GetChannelByName(channel.TeamId, channel.Name, false)
if channelGetErr != nil {
return "", errors.Wrap(channelGetErr, "failed to get channel by name")
}
}
// If channel exists, update the metadata
if existingChannel != nil {
return updateChannel(p, existingChannel, channel)
}
// Create a new channel
createdChannel, createChannelErr := p.API.CreateChannel(channel)
if createChannelErr != nil {
return "", errors.Wrap(createChannelErr, "failed to create channel")
}
// Set the new channel id in key value store
if kvSetErr := p.API.KVSet(CHANNEL_KEY, []byte(createdChannel.Id)); kvSetErr != nil {
p.API.LogWarn("Failed to set created channel id.", "channelid", createdChannel.Id, "err", kvSetErr)
}
return createdChannel.Id, nil
}
func updateChannel(p *HelpersImpl, existing *model.Channel, new *model.Channel) (string, error) {
// Update metadata of the channel
if updateErr := updateChannelMeta(existing, new); updateErr != nil {
return "", errors.Wrap(updateErr, "Failed to update the metadata of existing channel")
}
// Send the updates to API
updatedChannel, channelUpdateErr := p.API.UpdateChannel(existing)
if channelUpdateErr != nil {
return "", errors.Wrap(channelUpdateErr, "Failed to update the existing channel")
}
// Channel exists!
return updatedChannel.Id, nil
}
func updateChannelMeta(existing *model.Channel, new *model.Channel) error {
// Check if channels are of different types
if existing.Type != new.Type {
return errors.New("Channel type cannot be updated")
}
// Update metadata of channel
existing.Name = new.Name
existing.DisplayName = new.DisplayName
existing.Purpose = new.Purpose
existing.Header = new.Header
return nil
}

297
plugin/helpers_channels_test.go Обычный файл
Просмотреть файл

@@ -0,0 +1,297 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package plugin_test
import (
"testing"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/plugin"
"github.com/mattermost/mattermost-server/plugin/plugintest"
"github.com/stretchr/testify/assert"
)
func TestEnsureChannel(t *testing.T) {
setupAPI := func() *plugintest.API {
return &plugintest.API{}
}
testChannel := &model.Channel{
Id: model.NewId(),
TeamId: model.NewId(),
Type: "public",
Name: "test_channel",
DisplayName: "Test Channel",
Purpose: "Testing EnsureChannel",
Header: "Testing EnsureChannel",
}
t.Run("bad parameters", func(t *testing.T) {
t.Run("no channel", func(t *testing.T) {
p := &plugin.HelpersImpl{}
channelId, err := p.EnsureChannel(nil)
assert.Equal(t, "", channelId)
assert.NotNil(t, err)
})
t.Run("empty name", func(t *testing.T) {
p := &plugin.HelpersImpl{}
channelId, err := p.EnsureChannel(&model.Channel{
Name: "",
})
assert.Equal(t, "", channelId)
assert.NotNil(t, err)
})
t.Run("name without teamId", func(t *testing.T) {
p := &plugin.HelpersImpl{}
channelId, err := p.EnsureChannel(&model.Channel{
Name: "test_channel",
})
assert.Equal(t, "", channelId)
assert.NotNil(t, err)
})
t.Run("teamId without name", func(t *testing.T) {
p := &plugin.HelpersImpl{}
channelId, err := p.EnsureChannel(&model.Channel{
TeamId: model.NewId(),
})
assert.Equal(t, "", channelId)
assert.NotNil(t, err)
})
t.Run("teamId with empty name", func(t *testing.T) {
p := &plugin.HelpersImpl{}
channelId, err := p.EnsureChannel(&model.Channel{
TeamId: model.NewId(),
})
assert.Equal(t, "", channelId)
assert.NotNil(t, err)
})
})
t.Run("if channel already exists in Key Value store", func(t *testing.T) {
t.Run("should return an error if unable to get channel id", func(t *testing.T) {
api := setupAPI()
api.On("KVGet", plugin.CHANNEL_KEY).Return(nil, &model.AppError{})
defer api.AssertExpectations(t)
p := &plugin.HelpersImpl{API: api}
channelId, err := p.EnsureChannel(testChannel)
assert.Equal(t, "", channelId)
assert.NotNil(t, err)
})
t.Run("should return an error if unable to get channel", func(t *testing.T) {
expectedChannelId := model.NewId()
api := setupAPI()
api.On("KVGet", plugin.CHANNEL_KEY).Return([]byte(expectedChannelId), nil)
api.On("GetChannel", expectedChannelId).Return(nil, &model.AppError{})
p := &plugin.HelpersImpl{API: api}
channelId, err := p.EnsureChannel(testChannel)
assert.Equal(t, "", channelId)
assert.NotNil(t, err)
})
t.Run("should return an error if unable to update channel", func(t *testing.T) {
expectedChannelId := model.NewId()
api := setupAPI()
api.On("KVGet", plugin.CHANNEL_KEY).Return([]byte(expectedChannelId), nil)
api.On("GetChannel", expectedChannelId).Return(testChannel, nil)
api.On("UpdateChannel", testChannel).Return(nil, &model.AppError{})
defer api.AssertExpectations(t)
p := &plugin.HelpersImpl{API: api}
channelId, err := p.EnsureChannel(testChannel)
assert.Equal(t, "", channelId)
assert.NotNil(t, err)
})
t.Run("should return the Id of existing channel if metadata is same", func(t *testing.T) {
api := setupAPI()
api.On("KVGet", plugin.CHANNEL_KEY).Return([]byte(testChannel.Id), nil)
api.On("GetChannel", testChannel.Id).Return(testChannel, nil)
api.On("UpdateChannel", testChannel).Return(testChannel, nil)
defer api.AssertExpectations(t)
p := &plugin.HelpersImpl{API: api}
channelId, err := p.EnsureChannel(testChannel)
assert.Equal(t, testChannel.Id, channelId)
assert.Nil(t, err)
})
t.Run("should return error if channel type is different from existing one", func(t *testing.T) {
privChannel := &model.Channel{
Id: model.NewId(),
Type: "private",
TeamId: testChannel.TeamId,
Name: testChannel.Name,
}
api := setupAPI()
api.On("KVGet", plugin.CHANNEL_KEY).Return([]byte(testChannel.Id), nil)
api.On("GetChannel", testChannel.Id).Return(privChannel, nil)
defer api.AssertExpectations(t)
p := &plugin.HelpersImpl{API: api}
channelId, err := p.EnsureChannel(testChannel)
assert.Equal(t, "", channelId)
assert.NotNil(t, err)
})
t.Run("should return the Id of updated channel if metadata is different", func(t *testing.T) {
updatedChannel := &model.Channel{
Id: model.NewId(),
TeamId: testChannel.TeamId,
Name: testChannel.Name,
}
api := setupAPI()
api.On("KVGet", plugin.CHANNEL_KEY).Return([]byte(testChannel.Id), nil)
api.On("GetChannel", testChannel.Id).Return(testChannel, nil)
api.On("UpdateChannel", testChannel).Return(updatedChannel, nil)
defer api.AssertExpectations(t)
p := &plugin.HelpersImpl{API: api}
channelId, err := p.EnsureChannel(testChannel)
assert.Equal(t, updatedChannel.Id, channelId)
assert.Nil(t, err)
})
})
t.Run("if channel is not in Key Value store but already exists", func(t *testing.T) {
t.Run("should return an error if unable to get channel", func(t *testing.T) {
api := setupAPI()
api.On("KVGet", plugin.CHANNEL_KEY).Return(nil, nil)
api.On("GetChannelByName", testChannel.TeamId, testChannel.Name, false).Return(nil, &model.AppError{})
defer api.AssertExpectations(t)
p := &plugin.HelpersImpl{API: api}
channelId, err := p.EnsureChannel(testChannel)
assert.Equal(t, "", channelId)
assert.NotNil(t, err)
})
t.Run("should return the Id of existing channel if metadata is same", func(t *testing.T) {
api := setupAPI()
api.On("KVGet", plugin.CHANNEL_KEY).Return(nil, nil)
api.On("GetChannelByName", testChannel.TeamId, testChannel.Name, false).Return(testChannel, nil)
api.On("UpdateChannel", testChannel).Return(testChannel, nil)
defer api.AssertExpectations(t)
p := &plugin.HelpersImpl{API: api}
channelId, err := p.EnsureChannel(testChannel)
assert.Equal(t, testChannel.Id, channelId)
assert.Nil(t, err)
})
t.Run("should return error if failed to update the channel", func(t *testing.T) {
api := setupAPI()
api.On("KVGet", plugin.CHANNEL_KEY).Return(nil, nil)
api.On("GetChannelByName", testChannel.TeamId, testChannel.Name, false).Return(testChannel, nil)
api.On("UpdateChannel", testChannel).Return(nil, &model.AppError{})
defer api.AssertExpectations(t)
p := &plugin.HelpersImpl{API: api}
channelId, err := p.EnsureChannel(testChannel)
assert.Equal(t, "", channelId)
assert.NotNil(t, err)
})
t.Run("should return the Id of updated channel if metadata is different", func(t *testing.T) {
updatedChannel := &model.Channel{
Id: model.NewId(),
TeamId: testChannel.TeamId,
Name: testChannel.Name,
}
api := setupAPI()
api.On("KVGet", plugin.CHANNEL_KEY).Return(nil, nil)
api.On("GetChannelByName", testChannel.TeamId, testChannel.Name, false).Return(testChannel, nil)
api.On("UpdateChannel", testChannel).Return(updatedChannel, nil)
defer api.AssertExpectations(t)
p := &plugin.HelpersImpl{API: api}
channelId, err := p.EnsureChannel(testChannel)
assert.Equal(t, updatedChannel.Id, channelId)
assert.Nil(t, err)
})
t.Run("should return error if channel type is different from existing one", func(t *testing.T) {
privChannel := &model.Channel{
Id: model.NewId(),
Type: "private",
TeamId: testChannel.TeamId,
Name: testChannel.Name,
}
api := setupAPI()
api.On("KVGet", plugin.CHANNEL_KEY).Return(nil, nil)
api.On("GetChannelByName", testChannel.TeamId, testChannel.Name, false).Return(privChannel, nil)
defer api.AssertExpectations(t)
p := &plugin.HelpersImpl{API: api}
channelId, err := p.EnsureChannel(testChannel)
assert.Equal(t, "", channelId)
assert.NotNil(t, err)
})
})
t.Run("if channel does not exist", func(t *testing.T) {
t.Run("should create new channel and return the Id", func(t *testing.T) {
api := setupAPI()
api.On("KVGet", plugin.CHANNEL_KEY).Return(nil, nil)
api.On("GetChannelByName", testChannel.TeamId, testChannel.Name, false).Return(nil, nil)
api.On("CreateChannel", testChannel).Return(testChannel, nil)
api.On("KVSet", plugin.CHANNEL_KEY, []byte(testChannel.Id)).Return(nil)
defer api.AssertExpectations(t)
p := &plugin.HelpersImpl{API: api}
channelId, err := p.EnsureChannel(testChannel)
assert.Equal(t, testChannel.Id, channelId)
assert.Nil(t, err)
})
t.Run("should return error if unable to create new channel", func(t *testing.T) {
api := setupAPI()
api.On("KVGet", plugin.CHANNEL_KEY).Return(nil, nil)
api.On("GetChannelByName", testChannel.TeamId, testChannel.Name, false).Return(nil, nil)
api.On("CreateChannel", testChannel).Return(nil, &model.AppError{})
defer api.AssertExpectations(t)
p := &plugin.HelpersImpl{API: api}
channelId, err := p.EnsureChannel(testChannel)
assert.Equal(t, "", channelId)
assert.NotNil(t, err)
})
t.Run("should log and return id if unable to write to Key Value store", func(t *testing.T) {
api := setupAPI()
api.On("KVGet", plugin.CHANNEL_KEY).Return(nil, nil)
api.On("GetChannelByName", testChannel.TeamId, testChannel.Name, false).Return(nil, nil)
api.On("CreateChannel", testChannel).Return(testChannel, nil)
api.On("KVSet", plugin.CHANNEL_KEY, []byte(testChannel.Id)).Return(&model.AppError{})
api.On("LogWarn", "Failed to set created channel id.", "channelid", testChannel.Id, "err", &model.AppError{})
defer api.AssertExpectations(t)
p := &plugin.HelpersImpl{API: api}
channelId, err := p.EnsureChannel(testChannel)
assert.Equal(t, testChannel.Id, channelId)
assert.Nil(t, err)
})
})
}