mirror of
https://github.com/mattermost/mattermost.git
synced 2025-02-25 18:55:24 -06:00
PLT-8131 (part2) Add plugin key value store support (#7902)
* Add plugin key value store support * Add localization strings * Updates per feedback
This commit is contained in:
92
store/sqlstore/plugin_store.go
Normal file
92
store/sqlstore/plugin_store.go
Normal file
@@ -0,0 +1,92 @@
|
||||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package sqlstore
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
"github.com/mattermost/mattermost-server/store"
|
||||
)
|
||||
|
||||
type SqlPluginStore struct {
|
||||
SqlStore
|
||||
}
|
||||
|
||||
func NewSqlPluginStore(sqlStore SqlStore) store.PluginStore {
|
||||
s := &SqlPluginStore{sqlStore}
|
||||
|
||||
for _, db := range sqlStore.GetAllConns() {
|
||||
table := db.AddTableWithName(model.PluginKeyValue{}, "PluginKeyValueStore").SetKeys(false, "PluginId", "Key")
|
||||
table.ColMap("Value").SetMaxSize(8192)
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func (ps SqlPluginStore) CreateIndexesIfNotExists() {
|
||||
}
|
||||
|
||||
func (ps SqlPluginStore) SaveOrUpdate(kv *model.PluginKeyValue) store.StoreChannel {
|
||||
return store.Do(func(result *store.StoreResult) {
|
||||
if result.Err = kv.IsValid(); result.Err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if ps.DriverName() == model.DATABASE_DRIVER_POSTGRES {
|
||||
// Unfortunately PostgreSQL pre-9.5 does not have an atomic upsert, so we use
|
||||
// separate update and insert queries to accomplish our upsert
|
||||
if rowsAffected, err := ps.GetMaster().Update(kv); err != nil {
|
||||
result.Err = model.NewAppError("SqlPluginStore.SaveOrUpdate", "store.sql_plugin_store.save.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
} else if rowsAffected == 0 {
|
||||
// No rows were affected by the update, so let's try an insert
|
||||
if err := ps.GetMaster().Insert(kv); err != nil {
|
||||
// If the error is from unique constraints violation, it's the result of a
|
||||
// valid race and we can report success. Otherwise we have a real error and
|
||||
// need to return it
|
||||
if !IsUniqueConstraintError(err, []string{"PRIMARY", "PluginId", "Key", "PKey"}) {
|
||||
result.Err = model.NewAppError("SqlPluginStore.SaveOrUpdate", "store.sql_plugin_store.save.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if ps.DriverName() == model.DATABASE_DRIVER_MYSQL {
|
||||
if _, err := ps.GetMaster().Exec("INSERT INTO PluginKeyValueStore (PluginId, PKey, PValue) VALUES(:PluginId, :Key, :Value) ON DUPLICATE KEY UPDATE PValue = :Value", map[string]interface{}{"PluginId": kv.PluginId, "Key": kv.Key, "Value": kv.Value}); err != nil {
|
||||
result.Err = model.NewAppError("SqlPluginStore.SaveOrUpdate", "store.sql_plugin_store.save.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
result.Data = kv
|
||||
})
|
||||
}
|
||||
|
||||
func (ps SqlPluginStore) Get(pluginId, key string) store.StoreChannel {
|
||||
return store.Do(func(result *store.StoreResult) {
|
||||
var kv *model.PluginKeyValue
|
||||
|
||||
if err := ps.GetReplica().SelectOne(&kv, "SELECT * FROM PluginKeyValueStore WHERE PluginId = :PluginId AND PKey = :Key", map[string]interface{}{"PluginId": pluginId, "Key": key}); err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
result.Err = model.NewAppError("SqlPluginStore.Get", "store.sql_plugin_store.get.app_error", nil, fmt.Sprintf("plugin_id=%v, key=%v, err=%v", pluginId, key, err.Error()), http.StatusNotFound)
|
||||
} else {
|
||||
result.Err = model.NewAppError("SqlPluginStore.Get", "store.sql_plugin_store.get.app_error", nil, fmt.Sprintf("plugin_id=%v, key=%v, err=%v", pluginId, key, err.Error()), http.StatusInternalServerError)
|
||||
}
|
||||
} else {
|
||||
result.Data = kv
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (ps SqlPluginStore) Delete(pluginId, key string) store.StoreChannel {
|
||||
return store.Do(func(result *store.StoreResult) {
|
||||
if _, err := ps.GetMaster().Exec("DELETE FROM PluginKeyValueStore WHERE PluginId = :PluginId AND PKey = :Key", map[string]interface{}{"PluginId": pluginId, "Key": key}); err != nil {
|
||||
result.Err = model.NewAppError("SqlPluginStore.Delete", "store.sql_plugin_store.delete.app_error", nil, fmt.Sprintf("plugin_id=%v, key=%v, err=%v", pluginId, key, err.Error()), http.StatusInternalServerError)
|
||||
} else {
|
||||
result.Data = true
|
||||
}
|
||||
})
|
||||
}
|
||||
14
store/sqlstore/plugin_store_test.go
Normal file
14
store/sqlstore/plugin_store_test.go
Normal file
@@ -0,0 +1,14 @@
|
||||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package sqlstore
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/mattermost/mattermost-server/store/storetest"
|
||||
)
|
||||
|
||||
func TestPluginStore(t *testing.T) {
|
||||
StoreTest(t, storetest.TestPluginStore)
|
||||
}
|
||||
@@ -85,5 +85,6 @@ type SqlStore interface {
|
||||
FileInfo() store.FileInfoStore
|
||||
Reaction() store.ReactionStore
|
||||
Job() store.JobStore
|
||||
Plugin() store.PluginStore
|
||||
UserAccessToken() store.UserAccessTokenStore
|
||||
}
|
||||
|
||||
@@ -84,6 +84,7 @@ type SqlSupplierOldStores struct {
|
||||
reaction store.ReactionStore
|
||||
job store.JobStore
|
||||
userAccessToken store.UserAccessTokenStore
|
||||
plugin store.PluginStore
|
||||
}
|
||||
|
||||
type SqlSupplier struct {
|
||||
@@ -129,6 +130,7 @@ func NewSqlSupplier(settings model.SqlSettings, metrics einterfaces.MetricsInter
|
||||
supplier.oldStores.fileInfo = NewSqlFileInfoStore(supplier, metrics)
|
||||
supplier.oldStores.job = NewSqlJobStore(supplier)
|
||||
supplier.oldStores.userAccessToken = NewSqlUserAccessTokenStore(supplier)
|
||||
supplier.oldStores.plugin = NewSqlPluginStore(supplier)
|
||||
|
||||
initSqlSupplierReactions(supplier)
|
||||
|
||||
@@ -161,6 +163,7 @@ func NewSqlSupplier(settings model.SqlSettings, metrics einterfaces.MetricsInter
|
||||
supplier.oldStores.fileInfo.(*SqlFileInfoStore).CreateIndexesIfNotExists()
|
||||
supplier.oldStores.job.(*SqlJobStore).CreateIndexesIfNotExists()
|
||||
supplier.oldStores.userAccessToken.(*SqlUserAccessTokenStore).CreateIndexesIfNotExists()
|
||||
supplier.oldStores.plugin.(*SqlPluginStore).CreateIndexesIfNotExists()
|
||||
|
||||
supplier.oldStores.preference.(*SqlPreferenceStore).DeleteUnusedFeatures()
|
||||
|
||||
@@ -798,6 +801,10 @@ func (ss *SqlSupplier) UserAccessToken() store.UserAccessTokenStore {
|
||||
return ss.oldStores.userAccessToken
|
||||
}
|
||||
|
||||
func (ss *SqlSupplier) Plugin() store.PluginStore {
|
||||
return ss.oldStores.plugin
|
||||
}
|
||||
|
||||
func (ss *SqlSupplier) DropAllTables() {
|
||||
ss.master.TruncateTables()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user