summaryrefslogtreecommitdiffstats
path: root/plugin
diff options
context:
space:
mode:
authorJoram Wilander <jwawilander@gmail.com>2017-11-27 17:23:35 -0500
committerGitHub <noreply@github.com>2017-11-27 17:23:35 -0500
commit6176bcff6977bda71f4fde10a52dde6d7d7ceb9a (patch)
treeb4a4a22879f4b88ffc4fb59f46ca69d441569ddd /plugin
parente85ec3830164ffdfbe8fd5696ab99446b38a01ef (diff)
downloadchat-6176bcff6977bda71f4fde10a52dde6d7d7ceb9a.tar.gz
chat-6176bcff6977bda71f4fde10a52dde6d7d7ceb9a.tar.bz2
chat-6176bcff6977bda71f4fde10a52dde6d7d7ceb9a.zip
PLT-8131 (part2) Add plugin key value store support (#7902)
* Add plugin key value store support * Add localization strings * Updates per feedback
Diffstat (limited to 'plugin')
-rw-r--r--plugin/api.go16
-rw-r--r--plugin/plugintest/api.go38
-rw-r--r--plugin/rpcplugin/api.go85
-rw-r--r--plugin/rpcplugin/api_test.go18
4 files changed, 150 insertions, 7 deletions
diff --git a/plugin/api.go b/plugin/api.go
index 8d27bc794..4bcfd112b 100644
--- a/plugin/api.go
+++ b/plugin/api.go
@@ -79,6 +79,20 @@ type API interface {
// GetPost gets a post.
GetPost(postId string) (*model.Post, *model.AppError)
- // Update post updates a post.
+ // UpdatePost updates a post.
UpdatePost(post *model.Post) (*model.Post, *model.AppError)
+
+ // KeyValueStore returns an object for accessing the persistent key value storage.
+ KeyValueStore() KeyValueStore
+}
+
+type KeyValueStore interface {
+ // Set will store a key-value pair, unique per plugin.
+ Set(key string, value []byte) *model.AppError
+
+ // Get will retrieve a value based on the key. Returns nil for non-existent keys.
+ Get(key string) ([]byte, *model.AppError)
+
+ // Delete will remove a key-value pair. Returns nil for non-existent keys.
+ Delete(key string) *model.AppError
}
diff --git a/plugin/plugintest/api.go b/plugin/plugintest/api.go
index c0e77648b..37052b4cf 100644
--- a/plugin/plugintest/api.go
+++ b/plugin/plugintest/api.go
@@ -12,9 +12,15 @@ import (
type API struct {
mock.Mock
+ Store *KeyValueStore
+}
+
+type KeyValueStore struct {
+ mock.Mock
}
var _ plugin.API = (*API)(nil)
+var _ plugin.KeyValueStore = (*KeyValueStore)(nil)
func (m *API) LoadPluginConfiguration(dest interface{}) error {
return m.Called(dest).Error(0)
@@ -235,3 +241,35 @@ func (m *API) UpdatePost(post *model.Post) (*model.Post, *model.AppError) {
err, _ := ret.Get(1).(*model.AppError)
return postOut, err
}
+
+func (m *API) KeyValueStore() plugin.KeyValueStore {
+ return m.Store
+}
+
+func (m *KeyValueStore) Set(key string, value []byte) *model.AppError {
+ ret := m.Called(key, value)
+ if f, ok := ret.Get(0).(func(string, []byte) *model.AppError); ok {
+ return f(key, value)
+ }
+ err, _ := ret.Get(0).(*model.AppError)
+ return err
+}
+
+func (m *KeyValueStore) Get(key string) ([]byte, *model.AppError) {
+ ret := m.Called(key)
+ if f, ok := ret.Get(0).(func(string) ([]byte, *model.AppError)); ok {
+ return f(key)
+ }
+ psv, _ := ret.Get(0).([]byte)
+ err, _ := ret.Get(1).(*model.AppError)
+ return psv, err
+}
+
+func (m *KeyValueStore) Delete(key string) *model.AppError {
+ ret := m.Called(key)
+ if f, ok := ret.Get(0).(func(string) *model.AppError); ok {
+ return f(key)
+ }
+ err, _ := ret.Get(0).(*model.AppError)
+ return err
+}
diff --git a/plugin/rpcplugin/api.go b/plugin/rpcplugin/api.go
index 98333b1d9..f2068e815 100644
--- a/plugin/rpcplugin/api.go
+++ b/plugin/rpcplugin/api.go
@@ -259,6 +259,41 @@ func (api *LocalAPI) UpdatePost(args *model.Post, reply *APIPostReply) error {
return nil
}
+type APIKeyValueStoreReply struct {
+ Value []byte
+ Error *model.AppError
+}
+
+type APIKeyValueStoreSetArgs struct {
+ Key string
+ Value []byte
+}
+
+func (api *LocalAPI) KeyValueStoreSet(args *APIKeyValueStoreSetArgs, reply *APIErrorReply) error {
+ err := api.api.KeyValueStore().Set(args.Key, args.Value)
+ *reply = APIErrorReply{
+ Error: err,
+ }
+ return nil
+}
+
+func (api *LocalAPI) KeyValueStoreGet(args string, reply *APIKeyValueStoreReply) error {
+ v, err := api.api.KeyValueStore().Get(args)
+ *reply = APIKeyValueStoreReply{
+ Value: v,
+ Error: err,
+ }
+ return nil
+}
+
+func (api *LocalAPI) KeyValueStoreDelete(args string, reply *APIErrorReply) error {
+ err := api.api.KeyValueStore().Delete(args)
+ *reply = APIErrorReply{
+ Error: err,
+ }
+ return nil
+}
+
func ServeAPI(api plugin.API, conn io.ReadWriteCloser, muxer *Muxer) {
server := rpc.NewServer()
server.Register(&LocalAPI{
@@ -269,11 +304,17 @@ func ServeAPI(api plugin.API, conn io.ReadWriteCloser, muxer *Muxer) {
}
type RemoteAPI struct {
- client *rpc.Client
- muxer *Muxer
+ client *rpc.Client
+ muxer *Muxer
+ keyValueStore *RemoteKeyValueStore
+}
+
+type RemoteKeyValueStore struct {
+ api *RemoteAPI
}
var _ plugin.API = (*RemoteAPI)(nil)
+var _ plugin.KeyValueStore = (*RemoteKeyValueStore)(nil)
func (api *RemoteAPI) LoadPluginConfiguration(dest interface{}) error {
var config []byte
@@ -467,13 +508,47 @@ func (api *RemoteAPI) UpdatePost(post *model.Post) (*model.Post, *model.AppError
return reply.Post, reply.Error
}
+func (api *RemoteAPI) KeyValueStore() plugin.KeyValueStore {
+ return api.keyValueStore
+}
+
+func (s *RemoteKeyValueStore) Set(key string, value []byte) *model.AppError {
+ var reply APIErrorReply
+ if err := s.api.client.Call("LocalAPI.KeyValueStoreSet", &APIKeyValueStoreSetArgs{Key: key, Value: value}, &reply); err != nil {
+ return model.NewAppError("RemoteAPI.KeyValueStoreSet", "plugin.rpcplugin.invocation.error", nil, "err="+err.Error(), http.StatusInternalServerError)
+ }
+ return reply.Error
+}
+
+func (s *RemoteKeyValueStore) Get(key string) ([]byte, *model.AppError) {
+ var reply APIKeyValueStoreReply
+ if err := s.api.client.Call("LocalAPI.KeyValueStoreGet", key, &reply); err != nil {
+ return nil, model.NewAppError("RemoteAPI.KeyValueStoreGet", "plugin.rpcplugin.invocation.error", nil, "err="+err.Error(), http.StatusInternalServerError)
+ }
+ return reply.Value, reply.Error
+}
+
+func (s *RemoteKeyValueStore) Delete(key string) *model.AppError {
+ var reply APIErrorReply
+ if err := s.api.client.Call("LocalAPI.KeyValueStoreDelete", key, &reply); err != nil {
+ return model.NewAppError("RemoteAPI.KeyValueStoreDelete", "plugin.rpcplugin.invocation.error", nil, "err="+err.Error(), http.StatusInternalServerError)
+ }
+ return reply.Error
+}
+
func (h *RemoteAPI) Close() error {
return h.client.Close()
}
func ConnectAPI(conn io.ReadWriteCloser, muxer *Muxer) *RemoteAPI {
- return &RemoteAPI{
- client: rpc.NewClient(conn),
- muxer: muxer,
+ remoteKeyValueStore := &RemoteKeyValueStore{}
+ remoteApi := &RemoteAPI{
+ client: rpc.NewClient(conn),
+ muxer: muxer,
+ keyValueStore: remoteKeyValueStore,
}
+
+ remoteKeyValueStore.api = remoteApi
+
+ return remoteApi
}
diff --git a/plugin/rpcplugin/api_test.go b/plugin/rpcplugin/api_test.go
index 080f2825f..0c7321162 100644
--- a/plugin/rpcplugin/api_test.go
+++ b/plugin/rpcplugin/api_test.go
@@ -34,7 +34,8 @@ func testAPIRPC(api plugin.API, f func(plugin.API)) {
}
func TestAPI(t *testing.T) {
- var api plugintest.API
+ keyValueStore := &plugintest.KeyValueStore{}
+ api := plugintest.API{Store: keyValueStore}
defer api.AssertExpectations(t)
type Config struct {
@@ -199,5 +200,20 @@ func TestAPI(t *testing.T) {
post, err = remote.UpdatePost(testPost)
assert.Equal(t, testPost, post)
assert.Nil(t, err)
+
+ api.KeyValueStore().(*plugintest.KeyValueStore).On("Set", "thekey", []byte("thevalue")).Return(nil).Once()
+ err = remote.KeyValueStore().Set("thekey", []byte("thevalue"))
+ assert.Nil(t, err)
+
+ api.KeyValueStore().(*plugintest.KeyValueStore).On("Get", "thekey").Return(func(key string) ([]byte, *model.AppError) {
+ return []byte("thevalue"), nil
+ }).Once()
+ ret, err := remote.KeyValueStore().Get("thekey")
+ assert.Nil(t, err)
+ assert.Equal(t, []byte("thevalue"), ret)
+
+ api.KeyValueStore().(*plugintest.KeyValueStore).On("Delete", "thekey").Return(nil).Once()
+ err = remote.KeyValueStore().Delete("thekey")
+ assert.Nil(t, err)
})
}