Merge pull request #2052 from mattermost/PLT-1429
PLT-1429 adding user created slash commands
Этот коммит содержится в:
173
store/sql_command_store.go
Обычный файл
173
store/sql_command_store.go
Обычный файл
@@ -0,0 +1,173 @@
|
||||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package store
|
||||
|
||||
import (
|
||||
"github.com/mattermost/platform/model"
|
||||
)
|
||||
|
||||
type SqlCommandStore struct {
|
||||
*SqlStore
|
||||
}
|
||||
|
||||
func NewSqlCommandStore(sqlStore *SqlStore) CommandStore {
|
||||
s := &SqlCommandStore{sqlStore}
|
||||
|
||||
for _, db := range sqlStore.GetAllConns() {
|
||||
tableo := db.AddTableWithName(model.Command{}, "Commands").SetKeys(false, "Id")
|
||||
tableo.ColMap("Id").SetMaxSize(26)
|
||||
tableo.ColMap("Token").SetMaxSize(26)
|
||||
tableo.ColMap("CreatorId").SetMaxSize(26)
|
||||
tableo.ColMap("TeamId").SetMaxSize(26)
|
||||
tableo.ColMap("Trigger").SetMaxSize(128)
|
||||
tableo.ColMap("URL").SetMaxSize(1024)
|
||||
tableo.ColMap("Method").SetMaxSize(1)
|
||||
tableo.ColMap("Username").SetMaxSize(64)
|
||||
tableo.ColMap("IconURL").SetMaxSize(1024)
|
||||
tableo.ColMap("AutoCompleteDesc").SetMaxSize(1024)
|
||||
tableo.ColMap("AutoCompleteHint").SetMaxSize(1024)
|
||||
tableo.ColMap("DisplayName").SetMaxSize(64)
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func (s SqlCommandStore) UpgradeSchemaIfNeeded() {
|
||||
}
|
||||
|
||||
func (s SqlCommandStore) CreateIndexesIfNotExists() {
|
||||
s.CreateIndexIfNotExists("idx_command_team_id", "Commands", "TeamId")
|
||||
}
|
||||
|
||||
func (s SqlCommandStore) Save(command *model.Command) StoreChannel {
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
result := StoreResult{}
|
||||
|
||||
if len(command.Id) > 0 {
|
||||
result.Err = model.NewLocAppError("SqlCommandStore.Save", "store.sql_command.save.saving_overwrite.app_error", nil, "id="+command.Id)
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
return
|
||||
}
|
||||
|
||||
command.PreSave()
|
||||
if result.Err = command.IsValid(); result.Err != nil {
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
return
|
||||
}
|
||||
|
||||
if err := s.GetMaster().Insert(command); err != nil {
|
||||
result.Err = model.NewLocAppError("SqlCommandStore.Save", "store.sql_command.save.saving.app_error", nil, "id="+command.Id+", "+err.Error())
|
||||
} else {
|
||||
result.Data = command
|
||||
}
|
||||
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
}()
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
|
||||
func (s SqlCommandStore) Get(id string) StoreChannel {
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
result := StoreResult{}
|
||||
|
||||
var command model.Command
|
||||
|
||||
if err := s.GetReplica().SelectOne(&command, "SELECT * FROM Commands WHERE Id = :Id AND DeleteAt = 0", map[string]interface{}{"Id": id}); err != nil {
|
||||
result.Err = model.NewLocAppError("SqlCommandStore.Get", "store.sql_command.save.get.app_error", nil, "id="+id+", err="+err.Error())
|
||||
}
|
||||
|
||||
result.Data = &command
|
||||
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
}()
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
|
||||
func (s SqlCommandStore) GetByTeam(teamId string) StoreChannel {
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
result := StoreResult{}
|
||||
|
||||
var commands []*model.Command
|
||||
|
||||
if _, err := s.GetReplica().Select(&commands, "SELECT * FROM Commands WHERE TeamId = :TeamId AND DeleteAt = 0", map[string]interface{}{"TeamId": teamId}); err != nil {
|
||||
result.Err = model.NewLocAppError("SqlCommandStore.GetByTeam", "store.sql_command.save.get_team.app_error", nil, "teamId="+teamId+", err="+err.Error())
|
||||
}
|
||||
|
||||
result.Data = commands
|
||||
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
}()
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
|
||||
func (s SqlCommandStore) Delete(commandId string, time int64) StoreChannel {
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
result := StoreResult{}
|
||||
|
||||
_, err := s.GetMaster().Exec("Update Commands SET DeleteAt = :DeleteAt, UpdateAt = :UpdateAt WHERE Id = :Id", map[string]interface{}{"DeleteAt": time, "UpdateAt": time, "Id": commandId})
|
||||
if err != nil {
|
||||
result.Err = model.NewLocAppError("SqlCommandStore.Delete", "store.sql_command.save.delete.app_error", nil, "id="+commandId+", err="+err.Error())
|
||||
}
|
||||
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
}()
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
|
||||
func (s SqlCommandStore) PermanentDeleteByUser(userId string) StoreChannel {
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
result := StoreResult{}
|
||||
|
||||
_, err := s.GetMaster().Exec("DELETE FROM Commands WHERE CreatorId = :UserId", map[string]interface{}{"UserId": userId})
|
||||
if err != nil {
|
||||
result.Err = model.NewLocAppError("SqlCommandStore.DeleteByUser", "store.sql_command.save.delete_perm.app_error", nil, "id="+userId+", err="+err.Error())
|
||||
}
|
||||
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
}()
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
|
||||
func (s SqlCommandStore) Update(hook *model.Command) StoreChannel {
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
result := StoreResult{}
|
||||
|
||||
hook.UpdateAt = model.GetMillis()
|
||||
|
||||
if _, err := s.GetMaster().Update(hook); err != nil {
|
||||
result.Err = model.NewLocAppError("SqlCommandStore.Update", "store.sql_command.save.update.app_error", nil, "id="+hook.Id+", "+err.Error())
|
||||
} else {
|
||||
result.Data = hook
|
||||
}
|
||||
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
}()
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
155
store/sql_command_store_test.go
Обычный файл
155
store/sql_command_store_test.go
Обычный файл
@@ -0,0 +1,155 @@
|
||||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package store
|
||||
|
||||
import (
|
||||
"github.com/mattermost/platform/model"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCommandStoreSave(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
o1 := model.Command{}
|
||||
o1.CreatorId = model.NewId()
|
||||
o1.Method = model.COMMAND_METHOD_POST
|
||||
o1.TeamId = model.NewId()
|
||||
o1.URL = "http://nowhere.com/"
|
||||
|
||||
if err := (<-store.Command().Save(&o1)).Err; err != nil {
|
||||
t.Fatal("couldn't save item", err)
|
||||
}
|
||||
|
||||
if err := (<-store.Command().Save(&o1)).Err; err == nil {
|
||||
t.Fatal("shouldn't be able to update from save")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommandStoreGet(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
o1 := &model.Command{}
|
||||
o1.CreatorId = model.NewId()
|
||||
o1.Method = model.COMMAND_METHOD_POST
|
||||
o1.TeamId = model.NewId()
|
||||
o1.URL = "http://nowhere.com/"
|
||||
|
||||
o1 = (<-store.Command().Save(o1)).Data.(*model.Command)
|
||||
|
||||
if r1 := <-store.Command().Get(o1.Id); r1.Err != nil {
|
||||
t.Fatal(r1.Err)
|
||||
} else {
|
||||
if r1.Data.(*model.Command).CreateAt != o1.CreateAt {
|
||||
t.Fatal("invalid returned command")
|
||||
}
|
||||
}
|
||||
|
||||
if err := (<-store.Command().Get("123")).Err; err == nil {
|
||||
t.Fatal("Missing id should have failed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommandStoreGetByTeam(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
o1 := &model.Command{}
|
||||
o1.CreatorId = model.NewId()
|
||||
o1.Method = model.COMMAND_METHOD_POST
|
||||
o1.TeamId = model.NewId()
|
||||
o1.URL = "http://nowhere.com/"
|
||||
|
||||
o1 = (<-store.Command().Save(o1)).Data.(*model.Command)
|
||||
|
||||
if r1 := <-store.Command().GetByTeam(o1.TeamId); r1.Err != nil {
|
||||
t.Fatal(r1.Err)
|
||||
} else {
|
||||
if r1.Data.([]*model.Command)[0].CreateAt != o1.CreateAt {
|
||||
t.Fatal("invalid returned command")
|
||||
}
|
||||
}
|
||||
|
||||
if result := <-store.Command().GetByTeam("123"); result.Err != nil {
|
||||
t.Fatal(result.Err)
|
||||
} else {
|
||||
if len(result.Data.([]*model.Command)) != 0 {
|
||||
t.Fatal("no commands should have returned")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommandStoreDelete(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
o1 := &model.Command{}
|
||||
o1.CreatorId = model.NewId()
|
||||
o1.Method = model.COMMAND_METHOD_POST
|
||||
o1.TeamId = model.NewId()
|
||||
o1.URL = "http://nowhere.com/"
|
||||
|
||||
o1 = (<-store.Command().Save(o1)).Data.(*model.Command)
|
||||
|
||||
if r1 := <-store.Command().Get(o1.Id); r1.Err != nil {
|
||||
t.Fatal(r1.Err)
|
||||
} else {
|
||||
if r1.Data.(*model.Command).CreateAt != o1.CreateAt {
|
||||
t.Fatal("invalid returned command")
|
||||
}
|
||||
}
|
||||
|
||||
if r2 := <-store.Command().Delete(o1.Id, model.GetMillis()); r2.Err != nil {
|
||||
t.Fatal(r2.Err)
|
||||
}
|
||||
|
||||
if r3 := (<-store.Command().Get(o1.Id)); r3.Err == nil {
|
||||
t.Log(r3.Data)
|
||||
t.Fatal("Missing id should have failed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommandStoreDeleteByUser(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
o1 := &model.Command{}
|
||||
o1.CreatorId = model.NewId()
|
||||
o1.Method = model.COMMAND_METHOD_POST
|
||||
o1.TeamId = model.NewId()
|
||||
o1.URL = "http://nowhere.com/"
|
||||
|
||||
o1 = (<-store.Command().Save(o1)).Data.(*model.Command)
|
||||
|
||||
if r1 := <-store.Command().Get(o1.Id); r1.Err != nil {
|
||||
t.Fatal(r1.Err)
|
||||
} else {
|
||||
if r1.Data.(*model.Command).CreateAt != o1.CreateAt {
|
||||
t.Fatal("invalid returned command")
|
||||
}
|
||||
}
|
||||
|
||||
if r2 := <-store.Command().PermanentDeleteByUser(o1.CreatorId); r2.Err != nil {
|
||||
t.Fatal(r2.Err)
|
||||
}
|
||||
|
||||
if r3 := (<-store.Command().Get(o1.Id)); r3.Err == nil {
|
||||
t.Log(r3.Data)
|
||||
t.Fatal("Missing id should have failed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommandStoreUpdate(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
o1 := &model.Command{}
|
||||
o1.CreatorId = model.NewId()
|
||||
o1.Method = model.COMMAND_METHOD_POST
|
||||
o1.TeamId = model.NewId()
|
||||
o1.URL = "http://nowhere.com/"
|
||||
|
||||
o1 = (<-store.Command().Save(o1)).Data.(*model.Command)
|
||||
|
||||
o1.Token = model.NewId()
|
||||
|
||||
if r2 := <-store.Command().Update(o1); r2.Err != nil {
|
||||
t.Fatal(r2.Err)
|
||||
}
|
||||
}
|
||||
@@ -47,6 +47,7 @@ type SqlStore struct {
|
||||
oauth OAuthStore
|
||||
system SystemStore
|
||||
webhook WebhookStore
|
||||
command CommandStore
|
||||
preference PreferenceStore
|
||||
}
|
||||
|
||||
@@ -100,6 +101,7 @@ func NewSqlStore() Store {
|
||||
sqlStore.oauth = NewSqlOAuthStore(sqlStore)
|
||||
sqlStore.system = NewSqlSystemStore(sqlStore)
|
||||
sqlStore.webhook = NewSqlWebhookStore(sqlStore)
|
||||
sqlStore.command = NewSqlCommandStore(sqlStore)
|
||||
sqlStore.preference = NewSqlPreferenceStore(sqlStore)
|
||||
|
||||
err := sqlStore.master.CreateTablesIfNotExists()
|
||||
@@ -116,6 +118,7 @@ func NewSqlStore() Store {
|
||||
sqlStore.oauth.(*SqlOAuthStore).UpgradeSchemaIfNeeded()
|
||||
sqlStore.system.(*SqlSystemStore).UpgradeSchemaIfNeeded()
|
||||
sqlStore.webhook.(*SqlWebhookStore).UpgradeSchemaIfNeeded()
|
||||
sqlStore.command.(*SqlCommandStore).UpgradeSchemaIfNeeded()
|
||||
sqlStore.preference.(*SqlPreferenceStore).UpgradeSchemaIfNeeded()
|
||||
|
||||
sqlStore.team.(*SqlTeamStore).CreateIndexesIfNotExists()
|
||||
@@ -127,6 +130,7 @@ func NewSqlStore() Store {
|
||||
sqlStore.oauth.(*SqlOAuthStore).CreateIndexesIfNotExists()
|
||||
sqlStore.system.(*SqlSystemStore).CreateIndexesIfNotExists()
|
||||
sqlStore.webhook.(*SqlWebhookStore).CreateIndexesIfNotExists()
|
||||
sqlStore.command.(*SqlCommandStore).CreateIndexesIfNotExists()
|
||||
sqlStore.preference.(*SqlPreferenceStore).CreateIndexesIfNotExists()
|
||||
|
||||
sqlStore.preference.(*SqlPreferenceStore).DeleteUnusedFeatures()
|
||||
@@ -511,6 +515,10 @@ func (ss SqlStore) Webhook() WebhookStore {
|
||||
return ss.webhook
|
||||
}
|
||||
|
||||
func (ss SqlStore) Command() CommandStore {
|
||||
return ss.command
|
||||
}
|
||||
|
||||
func (ss SqlStore) Preference() PreferenceStore {
|
||||
return ss.preference
|
||||
}
|
||||
|
||||
@@ -134,7 +134,7 @@ func (s SqlWebhookStore) PermanentDeleteIncomingByUser(userId string) StoreChann
|
||||
return storeChannel
|
||||
}
|
||||
|
||||
func (s SqlWebhookStore) GetIncomingByUser(userId string) StoreChannel {
|
||||
func (s SqlWebhookStore) GetIncomingByTeam(teamId string) StoreChannel {
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
@@ -142,8 +142,8 @@ func (s SqlWebhookStore) GetIncomingByUser(userId string) StoreChannel {
|
||||
|
||||
var webhooks []*model.IncomingWebhook
|
||||
|
||||
if _, err := s.GetReplica().Select(&webhooks, "SELECT * FROM IncomingWebhooks WHERE UserId = :UserId AND DeleteAt = 0", map[string]interface{}{"UserId": userId}); err != nil {
|
||||
result.Err = model.NewLocAppError("SqlWebhookStore.GetIncomingByUser", "store.sql_webhooks.get_incoming_by_user.app_error", nil, "userId="+userId+", err="+err.Error())
|
||||
if _, err := s.GetReplica().Select(&webhooks, "SELECT * FROM IncomingWebhooks WHERE TeamId = :TeamId AND DeleteAt = 0", map[string]interface{}{"TeamId": teamId}); err != nil {
|
||||
result.Err = model.NewLocAppError("SqlWebhookStore.GetIncomingByUser", "store.sql_webhooks.get_incoming_by_user.app_error", nil, "teamId="+teamId+", err="+err.Error())
|
||||
}
|
||||
|
||||
result.Data = webhooks
|
||||
@@ -231,27 +231,6 @@ func (s SqlWebhookStore) GetOutgoing(id string) StoreChannel {
|
||||
return storeChannel
|
||||
}
|
||||
|
||||
func (s SqlWebhookStore) GetOutgoingByCreator(userId string) StoreChannel {
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
result := StoreResult{}
|
||||
|
||||
var webhooks []*model.OutgoingWebhook
|
||||
|
||||
if _, err := s.GetReplica().Select(&webhooks, "SELECT * FROM OutgoingWebhooks WHERE CreatorId = :UserId AND DeleteAt = 0", map[string]interface{}{"UserId": userId}); err != nil {
|
||||
result.Err = model.NewLocAppError("SqlWebhookStore.GetOutgoingByCreator", "store.sql_webhooks.get_outgoing_by_creator.app_error", nil, "userId="+userId+", err="+err.Error())
|
||||
}
|
||||
|
||||
result.Data = webhooks
|
||||
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
}()
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
|
||||
func (s SqlWebhookStore) GetOutgoingByChannel(channelId string) StoreChannel {
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ func TestWebhookStoreGetIncoming(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestWebhookStoreGetIncomingByUser(t *testing.T) {
|
||||
func TestWebhookStoreGetIncomingByTeam(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
o1 := &model.IncomingWebhook{}
|
||||
@@ -58,7 +58,7 @@ func TestWebhookStoreGetIncomingByUser(t *testing.T) {
|
||||
|
||||
o1 = (<-store.Webhook().SaveIncoming(o1)).Data.(*model.IncomingWebhook)
|
||||
|
||||
if r1 := <-store.Webhook().GetIncomingByUser(o1.UserId); r1.Err != nil {
|
||||
if r1 := <-store.Webhook().GetIncomingByTeam(o1.TeamId); r1.Err != nil {
|
||||
t.Fatal(r1.Err)
|
||||
} else {
|
||||
if r1.Data.([]*model.IncomingWebhook)[0].CreateAt != o1.CreateAt {
|
||||
@@ -66,7 +66,7 @@ func TestWebhookStoreGetIncomingByUser(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
if result := <-store.Webhook().GetIncomingByUser("123"); result.Err != nil {
|
||||
if result := <-store.Webhook().GetIncomingByTeam("123"); result.Err != nil {
|
||||
t.Fatal(result.Err)
|
||||
} else {
|
||||
if len(result.Data.([]*model.IncomingWebhook)) != 0 {
|
||||
@@ -201,34 +201,6 @@ func TestWebhookStoreGetOutgoingByChannel(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestWebhookStoreGetOutgoingByCreator(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
o1 := &model.OutgoingWebhook{}
|
||||
o1.ChannelId = model.NewId()
|
||||
o1.CreatorId = model.NewId()
|
||||
o1.TeamId = model.NewId()
|
||||
o1.CallbackURLs = []string{"http://nowhere.com/"}
|
||||
|
||||
o1 = (<-store.Webhook().SaveOutgoing(o1)).Data.(*model.OutgoingWebhook)
|
||||
|
||||
if r1 := <-store.Webhook().GetOutgoingByCreator(o1.CreatorId); r1.Err != nil {
|
||||
t.Fatal(r1.Err)
|
||||
} else {
|
||||
if r1.Data.([]*model.OutgoingWebhook)[0].CreateAt != o1.CreateAt {
|
||||
t.Fatal("invalid returned webhook")
|
||||
}
|
||||
}
|
||||
|
||||
if result := <-store.Webhook().GetOutgoingByCreator("123"); result.Err != nil {
|
||||
t.Fatal(result.Err)
|
||||
} else {
|
||||
if len(result.Data.([]*model.OutgoingWebhook)) != 0 {
|
||||
t.Fatal("no webhooks should have returned")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestWebhookStoreGetOutgoingByTeam(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ type Store interface {
|
||||
OAuth() OAuthStore
|
||||
System() SystemStore
|
||||
Webhook() WebhookStore
|
||||
Command() CommandStore
|
||||
Preference() PreferenceStore
|
||||
MarkSystemRanUnitTests()
|
||||
Close()
|
||||
@@ -170,13 +171,12 @@ type SystemStore interface {
|
||||
type WebhookStore interface {
|
||||
SaveIncoming(webhook *model.IncomingWebhook) StoreChannel
|
||||
GetIncoming(id string) StoreChannel
|
||||
GetIncomingByUser(userId string) StoreChannel
|
||||
GetIncomingByTeam(teamId string) StoreChannel
|
||||
GetIncomingByChannel(channelId string) StoreChannel
|
||||
DeleteIncoming(webhookId string, time int64) StoreChannel
|
||||
PermanentDeleteIncomingByUser(userId string) StoreChannel
|
||||
SaveOutgoing(webhook *model.OutgoingWebhook) StoreChannel
|
||||
GetOutgoing(id string) StoreChannel
|
||||
GetOutgoingByCreator(userId string) StoreChannel
|
||||
GetOutgoingByChannel(channelId string) StoreChannel
|
||||
GetOutgoingByTeam(teamId string) StoreChannel
|
||||
DeleteOutgoing(webhookId string, time int64) StoreChannel
|
||||
@@ -186,6 +186,15 @@ type WebhookStore interface {
|
||||
AnalyticsOutgoingCount(teamId string) StoreChannel
|
||||
}
|
||||
|
||||
type CommandStore interface {
|
||||
Save(webhook *model.Command) StoreChannel
|
||||
Get(id string) StoreChannel
|
||||
GetByTeam(teamId string) StoreChannel
|
||||
Delete(commandId string, time int64) StoreChannel
|
||||
PermanentDeleteByUser(userId string) StoreChannel
|
||||
Update(hook *model.Command) StoreChannel
|
||||
}
|
||||
|
||||
type PreferenceStore interface {
|
||||
Save(preferences *model.Preferences) StoreChannel
|
||||
Get(userId string, category string, name string) StoreChannel
|
||||
|
||||
Ссылка в новой задаче
Block a user