PLT-8131 (part2) Add plugin key value store support (#7902)

* Add plugin key value store support

* Add localization strings

* Updates per feedback
Этот коммит содержится в:
Joram Wilander
2017-11-27 17:23:35 -05:00
коммит произвёл GitHub
родитель e85ec38301
Коммит 6176bcff69
22 изменённых файлов: 888 добавлений и 230 удалений

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

@@ -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
}

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

@@ -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
}

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

@@ -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
}

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

@@ -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)
})
}