diff --git a/plugin/helpers.go b/plugin/helpers.go index b078844cf3..a0f77e4972 100644 --- a/plugin/helpers.go +++ b/plugin/helpers.go @@ -6,10 +6,26 @@ package plugin import "github.com/mattermost/mattermost-server/model" type Helpers interface { - // EnsureBot ether returns an existing bot user or creates a bot user with + // EnsureBot either returns an existing bot user or creates a bot user with // the specifications of the passed bot. // Returns the id of the bot created or existing. EnsureBot(bot *model.Bot) (string, error) + + // KVGetJSON retrievs a value based on the key. + KVGetJSON(key string, value interface{}) error + + // KVSetJSON stores a key-value pair. + KVSetJSON(key string, value interface{}) error + + // KVCompareAndSetJSON updates a key-value pair if the current + // value is equal to oldValue. + // Returns (false, err) if DB/marshal/unmarshal error occurred + // Returns (false, nil) if current value != old value + // Returns (true, nil) if current value == old value or new key is inserted + KVCompareAndSetJSON(key string, oldValue interface{}, newValue interface{}) (bool, error) + + // KVSetWithExpiryJSON stores a key-value pair with an expiry time. + KVSetWithExpiryJSON(key string, value interface{}, expireInSeconds int64) error } type HelpersImpl struct { diff --git a/plugin/helpers_bots.go b/plugin/helpers_bots.go index 664a954d5d..4dac270dca 100644 --- a/plugin/helpers_bots.go +++ b/plugin/helpers_bots.go @@ -4,6 +4,7 @@ package plugin import ( + "encoding/json" "time" "github.com/mattermost/mattermost-server/model" @@ -64,3 +65,42 @@ func (p *HelpersImpl) EnsureBot(bot *model.Bot) (retBotId string, retErr error) return createdBot.UserId, nil } + +func (p *HelpersImpl) KVGetJSON(key string, value interface{}) error { + data, err := p.API.KVGet(key) + if err != nil { + return err + } + + return json.Unmarshal(data, value) +} + +func (p *HelpersImpl) KVSetJSON(key string, value interface{}) error { + data, err := json.Marshal(value) + if err != nil { + return err + } + return p.API.KVSet(key, data) +} + +func (p *HelpersImpl) KVCompareAndSetJSON(key string, oldValue interface{}, newValue interface{}) (bool, error) { + oldData, err := json.Marshal(oldValue) + if err != nil { + return false, errors.Wrap(err, "unable to marshal old value") + } + + newData, err := json.Marshal(newValue) + if err != nil { + return false, errors.Wrap(err, "unable to marshal new value") + } + + return p.API.KVCompareAndSet(key, oldData, newData) +} + +func (p *HelpersImpl) KVSetWithExpiryJSON(key string, value interface{}, expireInSeconds int64) error { + data, err := json.Marshal(value) + if err != nil { + return err + } + return p.API.KVSetWithExpiry(key, data, expireInSeconds) +} diff --git a/plugin/helpers_bots_test.go b/plugin/helpers_bots_test.go index 776c32a10b..cbd1b2405b 100644 --- a/plugin/helpers_bots_test.go +++ b/plugin/helpers_bots_test.go @@ -153,3 +153,179 @@ func TestEnsureBot(t *testing.T) { }) }) } + +func TestKVGetJSON(t *testing.T) { + setupAPI := func() *plugintest.API { + return &plugintest.API{} + } + + t.Run("KVGet error", func(t *testing.T) { + p := &plugin.HelpersImpl{} + + api := setupAPI() + api.On("KVGet", plugin.BOT_USER_KEY).Return(nil, &model.AppError{}) + p.API = api + + var dat map[string]interface{} + + err := p.KVGetJSON(plugin.BOT_USER_KEY, dat) + + api.AssertExpectations(t) + assert.NotNil(t, err) + assert.Nil(t, dat) + }) + + t.Run("Malformed JSON", func(t *testing.T) { + key := "test-key" + + p := &plugin.HelpersImpl{} + + api := setupAPI() + api.On("KVGet", key).Return([]byte(`{{:}"val-a": 10}`), nil) + p.API = api + + var dat map[string]interface{} + + err := p.KVGetJSON(key, &dat) + + api.AssertExpectations(t) + assert.NotNil(t, err) + assert.Nil(t, dat) + }) + + t.Run("Valid parameters passed (happy-path)", func(t *testing.T) { + key := "test-key" + + p := &plugin.HelpersImpl{} + + api := setupAPI() + api.On("KVGet", key).Return([]byte(`{"val-a": 10}`), nil) + p.API = api + + var dat map[string]interface{} + + err := p.KVGetJSON(key, &dat) + + api.AssertExpectations(t) + assert.Nil(t, err) + assert.Equal(t, map[string]interface{}{ + "val-a": float64(10), + }, dat) + }) +} + +func TestKVSetJSON(t *testing.T) { + key := "test-key" + + setupAPI := func() *plugintest.API { + return &plugintest.API{} + } + + t.Run("JSON Marshal error", func(t *testing.T) { + api := setupAPI() + api.AssertNotCalled(t, "KVSet") + + p := &plugin.HelpersImpl{API: api} + + err := p.KVSetJSON(key, func() { return }) + + api.AssertExpectations(t) + assert.NotNil(t, err) + }) + + t.Run("Valid parameters passed (Happy-path)", func(t *testing.T) { + api := setupAPI() + api.On("KVSet", key, []byte(`{"val-a":10}`)).Return(nil) + + p := &plugin.HelpersImpl{API: api} + + err := p.KVSetJSON(key, map[string]interface{}{ + "val-a": float64(10), + }) + + api.AssertExpectations(t) + assert.Nil(t, err) + }) +} + +func TestKVCompareAndSetJSON(t *testing.T) { + key := "test-key" + setupAPI := func() *plugintest.API { + return &plugintest.API{} + } + + t.Run("old value JSON marshal error", func(t *testing.T) { + api := setupAPI() + api.AssertNotCalled(t, "KVCompareAndSet") + p := &plugin.HelpersImpl{API: api} + + ok, err := p.KVCompareAndSetJSON(key, func() { return }, map[string]interface{}{}) + + api.AssertExpectations(t) + assert.Equal(t, false, ok) + assert.NotNil(t, err) + }) + + t.Run("new value JSON marshal error", func(t *testing.T) { + api := setupAPI() + api.AssertNotCalled(t, "KVCompareAndSet") + + p := &plugin.HelpersImpl{API: api} + + ok, err := p.KVCompareAndSetJSON(key, map[string]interface{}{}, func() { return }) + + api.AssertExpectations(t) + assert.Equal(t, false, ok) + assert.NotNil(t, err) + }) + + t.Run("Valid parameters passed (happy-path)", func(t *testing.T) { + api := setupAPI() + api.On("KVCompareAndSet", key, []byte(`{"val-a":10}`), []byte(`{"val-b":20}`)).Return(false, nil) + p := &plugin.HelpersImpl{API: api} + + ok, err := p.KVCompareAndSetJSON(key, map[string]interface{}{ + "val-a": 10, + }, map[string]interface{}{ + "val-b": 20, + }) + + api.AssertExpectations(t) + assert.Equal(t, false, ok) + assert.Nil(t, err) + }) +} + +func TestKVSetWithExpiryJSON(t *testing.T) { + key := "test-key" + + setupAPI := func() *plugintest.API { + return &plugintest.API{} + } + + t.Run("JSON Marshal error", func(t *testing.T) { + api := setupAPI() + api.AssertNotCalled(t, "KVSetWithExpiry") + + p := &plugin.HelpersImpl{API: api} + + err := p.KVSetWithExpiryJSON(key, func() { return }, 100) + + api.AssertExpectations(t) + assert.NotNil(t, err) + }) + + t.Run("valid parameters passed (happy-path)", func(t *testing.T) { + api := setupAPI() + api.On("KVSetWithExpiry", key, []byte(`{"val-a":10}`), int64(100)).Return(nil) + + p := &plugin.HelpersImpl{API: api} + + err := p.KVSetWithExpiryJSON(key, map[string]interface{}{ + "val-a": float64(10), + }, 100) + + api.AssertExpectations(t) + assert.Nil(t, err) + }) +} diff --git a/plugin/plugintest/helpers.go b/plugin/plugintest/helpers.go index b77a4cea46..17fcd75970 100644 --- a/plugin/plugintest/helpers.go +++ b/plugin/plugintest/helpers.go @@ -32,3 +32,66 @@ func (_m *Helpers) EnsureBot(bot *model.Bot) (string, error) { return r0, r1 } + +// KVCompareAndSetJSON provides a mock function with given fields: key, oldValue, newValue +func (_m *Helpers) KVCompareAndSetJSON(key string, oldValue interface{}, newValue interface{}) (bool, error) { + ret := _m.Called(key, oldValue, newValue) + + var r0 bool + if rf, ok := ret.Get(0).(func(string, interface{}, interface{}) bool); ok { + r0 = rf(key, oldValue, newValue) + } else { + r0 = ret.Get(0).(bool) + } + + var r1 error + if rf, ok := ret.Get(1).(func(string, interface{}, interface{}) error); ok { + r1 = rf(key, oldValue, newValue) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// KVGetJSON provides a mock function with given fields: key, value +func (_m *Helpers) KVGetJSON(key string, value interface{}) error { + ret := _m.Called(key, value) + + var r0 error + if rf, ok := ret.Get(0).(func(string, interface{}) error); ok { + r0 = rf(key, value) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// KVSetJSON provides a mock function with given fields: key, value +func (_m *Helpers) KVSetJSON(key string, value interface{}) error { + ret := _m.Called(key, value) + + var r0 error + if rf, ok := ret.Get(0).(func(string, interface{}) error); ok { + r0 = rf(key, value) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// KVSetWithExpiryJSON provides a mock function with given fields: key, value, expireInSeconds +func (_m *Helpers) KVSetWithExpiryJSON(key string, value interface{}, expireInSeconds int64) error { + ret := _m.Called(key, value, expireInSeconds) + + var r0 error + if rf, ok := ret.Get(0).(func(string, interface{}, int64) error); ok { + r0 = rf(key, value, expireInSeconds) + } else { + r0 = ret.Error(0) + } + + return r0 +}