first commit
Этот коммит содержится в:
75
store/redis.go
Обычный файл
75
store/redis.go
Обычный файл
@@ -0,0 +1,75 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package store
|
||||
|
||||
import (
|
||||
l4g "code.google.com/p/log4go"
|
||||
"github.com/mattermost/platform/model"
|
||||
"github.com/mattermost/platform/utils"
|
||||
"gopkg.in/redis.v2"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var client *redis.Client
|
||||
|
||||
func RedisClient() *redis.Client {
|
||||
|
||||
if client == nil {
|
||||
|
||||
addr := utils.Cfg.RedisSettings.DataSource
|
||||
|
||||
client = redis.NewTCPClient(&redis.Options{
|
||||
Addr: addr,
|
||||
Password: "",
|
||||
DB: 0,
|
||||
PoolSize: utils.Cfg.RedisSettings.MaxOpenConns,
|
||||
})
|
||||
|
||||
l4g.Info("Pinging redis at '%v'", addr)
|
||||
pong, err := client.Ping().Result()
|
||||
|
||||
if err != nil {
|
||||
l4g.Critical("Failed to open redis connection to '%v' err:%v", addr, err)
|
||||
time.Sleep(time.Second)
|
||||
panic("Failed to open redis connection " + err.Error())
|
||||
}
|
||||
|
||||
if pong != "PONG" {
|
||||
l4g.Critical("Failed to ping redis connection to '%v' err:%v", addr, err)
|
||||
time.Sleep(time.Second)
|
||||
panic("Failed to open ping connection " + err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
return client
|
||||
}
|
||||
|
||||
func RedisClose() {
|
||||
l4g.Info("Closing redis")
|
||||
|
||||
if client != nil {
|
||||
client.Close()
|
||||
client = nil
|
||||
}
|
||||
}
|
||||
|
||||
func PublishAndForget(message *model.Message) {
|
||||
|
||||
go func() {
|
||||
c := RedisClient()
|
||||
result := c.Publish(message.TeamId, message.ToJson())
|
||||
if result.Err() != nil {
|
||||
l4g.Error("Failed to publish message err=%v, payload=%v", result.Err(), message.ToJson())
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func GetMessageFromPayload(m interface{}) *model.Message {
|
||||
if msg, found := m.(*redis.Message); found {
|
||||
return model.MessageFromJson(strings.NewReader(msg.Payload))
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
59
store/redis_test.go
Обычный файл
59
store/redis_test.go
Обычный файл
@@ -0,0 +1,59 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package store
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/mattermost/platform/model"
|
||||
"github.com/mattermost/platform/utils"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRedis(t *testing.T) {
|
||||
utils.LoadConfig("config.json")
|
||||
|
||||
c := RedisClient()
|
||||
|
||||
if c == nil {
|
||||
t.Fatal("should have a valid redis connection")
|
||||
}
|
||||
|
||||
pubsub := c.PubSub()
|
||||
defer pubsub.Close()
|
||||
|
||||
m := model.NewMessage(model.NewId(), model.NewId(), model.NewId(), model.ACTION_TYPING)
|
||||
m.Add("RootId", model.NewId())
|
||||
|
||||
err := pubsub.Subscribe(m.TeamId)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// should be the subscribe success message
|
||||
// lets gobble that up
|
||||
if _, err := pubsub.Receive(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
PublishAndForget(m)
|
||||
|
||||
fmt.Println("here1")
|
||||
|
||||
if msg, err := pubsub.Receive(); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
|
||||
rmsg := GetMessageFromPayload(msg)
|
||||
|
||||
if m.TeamId != rmsg.TeamId {
|
||||
t.Fatal("Ids do not match")
|
||||
}
|
||||
|
||||
if m.Props["RootId"] != rmsg.Props["RootId"] {
|
||||
t.Fatal("Ids do not match")
|
||||
}
|
||||
}
|
||||
|
||||
RedisClose()
|
||||
}
|
||||
88
store/sql_audit_store.go
Обычный файл
88
store/sql_audit_store.go
Обычный файл
@@ -0,0 +1,88 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package store
|
||||
|
||||
import (
|
||||
"github.com/mattermost/platform/model"
|
||||
)
|
||||
|
||||
type SqlAuditStore struct {
|
||||
*SqlStore
|
||||
}
|
||||
|
||||
func NewSqlAuditStore(sqlStore *SqlStore) AuditStore {
|
||||
s := &SqlAuditStore{sqlStore}
|
||||
|
||||
for _, db := range sqlStore.GetAllConns() {
|
||||
table := db.AddTableWithName(model.Audit{}, "Audits").SetKeys(false, "Id")
|
||||
table.ColMap("Id").SetMaxSize(26)
|
||||
table.ColMap("UserId").SetMaxSize(26)
|
||||
table.ColMap("Action").SetMaxSize(64)
|
||||
table.ColMap("ExtraInfo").SetMaxSize(128)
|
||||
table.ColMap("IpAddress").SetMaxSize(64)
|
||||
table.ColMap("SessionId").SetMaxSize(26)
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func (s SqlAuditStore) UpgradeSchemaIfNeeded() {
|
||||
}
|
||||
|
||||
func (s SqlAuditStore) CreateIndexesIfNotExists() {
|
||||
s.CreateIndexIfNotExists("idx_user_id", "Audits", "UserId")
|
||||
}
|
||||
|
||||
func (s SqlAuditStore) Save(audit *model.Audit) StoreChannel {
|
||||
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
result := StoreResult{}
|
||||
|
||||
audit.Id = model.NewId()
|
||||
audit.CreateAt = model.GetMillis()
|
||||
|
||||
if err := s.GetMaster().Insert(audit); err != nil {
|
||||
result.Err = model.NewAppError("SqlAuditStore.Save",
|
||||
"We encounted an error saving the audit", "user_id="+
|
||||
audit.UserId+" action="+audit.Action)
|
||||
}
|
||||
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
}()
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
|
||||
func (s SqlAuditStore) Get(user_id string, limit int) StoreChannel {
|
||||
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
result := StoreResult{}
|
||||
|
||||
if limit > 1000 {
|
||||
limit = 1000
|
||||
result.Err = model.NewAppError("SqlAuditStore.Get", "Limit exceeded for paging", "user_id="+user_id)
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
return
|
||||
}
|
||||
|
||||
var audits model.Audits
|
||||
if _, err := s.GetReplica().Select(&audits, "SELECT * FROM Audits WHERE UserId = ? ORDER BY CreateAt DESC LIMIT ?",
|
||||
user_id, limit); err != nil {
|
||||
result.Err = model.NewAppError("SqlAuditStore.Get", "We encounted an error finding the audits", "user_id="+user_id)
|
||||
} else {
|
||||
result.Data = audits
|
||||
}
|
||||
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
}()
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
40
store/sql_audit_store_test.go
Обычный файл
40
store/sql_audit_store_test.go
Обычный файл
@@ -0,0 +1,40 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package store
|
||||
|
||||
import (
|
||||
"github.com/mattermost/platform/model"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSqlAuditStore(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
audit := &model.Audit{UserId: model.NewId(), IpAddress: "ipaddress", Action: "Action"}
|
||||
<-store.Audit().Save(audit)
|
||||
<-store.Audit().Save(audit)
|
||||
<-store.Audit().Save(audit)
|
||||
audit.ExtraInfo = "extra"
|
||||
<-store.Audit().Save(audit)
|
||||
|
||||
c := store.Audit().Get(audit.UserId, 100)
|
||||
result := <-c
|
||||
audits := result.Data.(model.Audits)
|
||||
|
||||
if len(audits) != 4 {
|
||||
t.Fatal("Failed to save and retrieve 4 audit logs")
|
||||
}
|
||||
|
||||
if audits[0].ExtraInfo != "extra" {
|
||||
t.Fatal("Failed to save property for extra info")
|
||||
}
|
||||
|
||||
c = store.Audit().Get("missing", 100)
|
||||
result = <-c
|
||||
audits = result.Data.(model.Audits)
|
||||
|
||||
if len(audits) != 0 {
|
||||
t.Fatal("Should have returned empty because user_id is missing")
|
||||
}
|
||||
}
|
||||
554
store/sql_channel_store.go
Обычный файл
554
store/sql_channel_store.go
Обычный файл
@@ -0,0 +1,554 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package store
|
||||
|
||||
import (
|
||||
"github.com/mattermost/platform/model"
|
||||
"github.com/mattermost/platform/utils"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type SqlChannelStore struct {
|
||||
*SqlStore
|
||||
}
|
||||
|
||||
func NewSqlChannelStore(sqlStore *SqlStore) ChannelStore {
|
||||
s := &SqlChannelStore{sqlStore}
|
||||
|
||||
for _, db := range sqlStore.GetAllConns() {
|
||||
table := db.AddTableWithName(model.Channel{}, "Channels").SetKeys(false, "Id")
|
||||
table.ColMap("Id").SetMaxSize(26)
|
||||
table.ColMap("TeamId").SetMaxSize(26)
|
||||
table.ColMap("Type").SetMaxSize(1)
|
||||
table.ColMap("DisplayName").SetMaxSize(64)
|
||||
table.ColMap("Name").SetMaxSize(64)
|
||||
table.SetUniqueTogether("Name", "TeamId")
|
||||
table.ColMap("Description").SetMaxSize(1024)
|
||||
|
||||
tablem := db.AddTableWithName(model.ChannelMember{}, "ChannelMembers").SetKeys(false, "ChannelId", "UserId")
|
||||
tablem.ColMap("ChannelId").SetMaxSize(26)
|
||||
tablem.ColMap("UserId").SetMaxSize(26)
|
||||
tablem.ColMap("Roles").SetMaxSize(64)
|
||||
tablem.ColMap("NotifyLevel").SetMaxSize(20)
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func (s SqlChannelStore) UpgradeSchemaIfNeeded() {
|
||||
}
|
||||
|
||||
func (s SqlChannelStore) CreateIndexesIfNotExists() {
|
||||
s.CreateIndexIfNotExists("idx_team_id", "Channels", "TeamId")
|
||||
}
|
||||
|
||||
func (s SqlChannelStore) Save(channel *model.Channel) StoreChannel {
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
result := StoreResult{}
|
||||
|
||||
if len(channel.Id) > 0 {
|
||||
result.Err = model.NewAppError("SqlChannelStore.Save",
|
||||
"Must call update for exisiting channel", "id="+channel.Id)
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
return
|
||||
}
|
||||
|
||||
channel.PreSave()
|
||||
if result.Err = channel.IsValid(); result.Err != nil {
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
return
|
||||
}
|
||||
|
||||
if count, err := s.GetMaster().SelectInt("SELECT COUNT(0) FROM Channels WHERE TeamId = ? AND DeleteAt = 0 AND (Type ='O' || Type ='P')", channel.TeamId); err != nil {
|
||||
result.Err = model.NewAppError("SqlChannelStore.Save", "Failed to get current channel count", "teamId="+channel.TeamId+", "+err.Error())
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
return
|
||||
} else if count > 150 {
|
||||
result.Err = model.NewAppError("SqlChannelStore.Save", "You've reached the limit of the number of allowed channels.", "teamId="+channel.TeamId)
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
return
|
||||
}
|
||||
|
||||
if err := s.GetMaster().Insert(channel); err != nil {
|
||||
if strings.Contains(err.Error(), "Duplicate entry") && strings.Contains(err.Error(), "for key 'Name'") {
|
||||
result.Err = model.NewAppError("SqlChannelStore.Save", "A channel with that name already exists", "id="+channel.Id+", "+err.Error())
|
||||
} else {
|
||||
result.Err = model.NewAppError("SqlChannelStore.Save", "We couldn't save the channel", "id="+channel.Id+", "+err.Error())
|
||||
}
|
||||
} else {
|
||||
result.Data = channel
|
||||
}
|
||||
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
}()
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
|
||||
func (s SqlChannelStore) Update(channel *model.Channel) StoreChannel {
|
||||
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
result := StoreResult{}
|
||||
|
||||
channel.PreUpdate()
|
||||
|
||||
if result.Err = channel.IsValid(); result.Err != nil {
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
return
|
||||
}
|
||||
|
||||
if count, err := s.GetMaster().Update(channel); err != nil {
|
||||
if strings.Contains(err.Error(), "Duplicate entry") && strings.Contains(err.Error(), "for key 'Name'") {
|
||||
result.Err = model.NewAppError("SqlChannelStore.Update", "A channel with that name already exists", "id="+channel.Id+", "+err.Error())
|
||||
} else {
|
||||
result.Err = model.NewAppError("SqlChannelStore.Update", "We encounted an error updating the channel", "id="+channel.Id+", "+err.Error())
|
||||
}
|
||||
} else if count != 1 {
|
||||
result.Err = model.NewAppError("SqlChannelStore.Update", "We couldn't update the channel", "id="+channel.Id)
|
||||
} else {
|
||||
result.Data = channel
|
||||
}
|
||||
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
}()
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
|
||||
func (s SqlChannelStore) Get(id string) StoreChannel {
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
result := StoreResult{}
|
||||
|
||||
if obj, err := s.GetReplica().Get(model.Channel{}, id); err != nil {
|
||||
result.Err = model.NewAppError("SqlChannelStore.Get", "We encounted an error finding the channel", "id="+id+", "+err.Error())
|
||||
} else if obj == nil {
|
||||
result.Err = model.NewAppError("SqlChannelStore.Get", "We couldn't find the existing channel", "id="+id)
|
||||
} else {
|
||||
result.Data = obj.(*model.Channel)
|
||||
}
|
||||
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
}()
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
|
||||
func (s SqlChannelStore) Delete(channelId string, time int64) StoreChannel {
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
result := StoreResult{}
|
||||
|
||||
_, err := s.GetMaster().Exec("Update Channels SET DeleteAt = ?, UpdateAt = ? WHERE Id = ?", time, time, channelId)
|
||||
if err != nil {
|
||||
result.Err = model.NewAppError("SqlChannelStore.Delete", "We couldn't delete the channel", "id="+channelId+", err="+err.Error())
|
||||
}
|
||||
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
}()
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
|
||||
type channelWithMember struct {
|
||||
model.Channel
|
||||
model.ChannelMember
|
||||
}
|
||||
|
||||
func (s SqlChannelStore) GetChannels(teamId string, userId string) StoreChannel {
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
result := StoreResult{}
|
||||
|
||||
var data []channelWithMember
|
||||
_, err := s.GetReplica().Select(&data, "SELECT * FROM Channels, ChannelMembers WHERE Id = ChannelId AND TeamId = ? AND UserId = ? AND DeleteAt = 0 ORDER BY DisplayName", teamId, userId)
|
||||
|
||||
if err != nil {
|
||||
result.Err = model.NewAppError("SqlChannelStore.GetChannels", "We couldn't get the channels", "teamId="+teamId+", userId="+userId+", err="+err.Error())
|
||||
} else {
|
||||
channels := &model.ChannelList{make([]*model.Channel, len(data)), make(map[string]*model.ChannelMember)}
|
||||
for i := range data {
|
||||
v := data[i]
|
||||
channels.Channels[i] = &v.Channel
|
||||
channels.Members[v.Channel.Id] = &v.ChannelMember
|
||||
}
|
||||
|
||||
if len(channels.Channels) == 0 {
|
||||
result.Err = model.NewAppError("SqlChannelStore.GetChannels", "No channels were found", "teamId="+teamId+", userId="+userId)
|
||||
} else {
|
||||
result.Data = channels
|
||||
}
|
||||
}
|
||||
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
}()
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
|
||||
func (s SqlChannelStore) GetMoreChannels(teamId string, userId string) StoreChannel {
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
result := StoreResult{}
|
||||
|
||||
var data []*model.Channel
|
||||
_, err := s.GetReplica().Select(&data,
|
||||
`SELECT
|
||||
*
|
||||
FROM
|
||||
Channels
|
||||
WHERE
|
||||
TeamId = ?
|
||||
AND Type IN ("O")
|
||||
AND DeleteAt = 0
|
||||
AND Id NOT IN (SELECT
|
||||
Channels.Id
|
||||
FROM
|
||||
Channels,
|
||||
ChannelMembers
|
||||
WHERE
|
||||
Id = ChannelId
|
||||
AND TeamId = ?
|
||||
AND UserId = ?
|
||||
AND DeleteAt = 0)
|
||||
ORDER BY DisplayName`,
|
||||
teamId, teamId, userId)
|
||||
|
||||
if err != nil {
|
||||
result.Err = model.NewAppError("SqlChannelStore.GetMoreChannels", "We couldn't get the channels", "teamId="+teamId+", userId="+userId+", err="+err.Error())
|
||||
} else {
|
||||
result.Data = &model.ChannelList{data, make(map[string]*model.ChannelMember)}
|
||||
}
|
||||
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
}()
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
|
||||
func (s SqlChannelStore) GetByName(teamId string, name string) StoreChannel {
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
result := StoreResult{}
|
||||
|
||||
channel := model.Channel{}
|
||||
|
||||
if err := s.GetReplica().SelectOne(&channel, "SELECT * FROM Channels WHERE TeamId=? AND Name=? AND DeleteAt = 0", teamId, name); err != nil {
|
||||
result.Err = model.NewAppError("SqlChannelStore.GetByName", "We couldn't find the existing channel", "teamId="+teamId+", "+"name="+name+", "+err.Error())
|
||||
} else {
|
||||
result.Data = &channel
|
||||
}
|
||||
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
}()
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
|
||||
func (s SqlChannelStore) SaveMember(member *model.ChannelMember) StoreChannel {
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
result := StoreResult{}
|
||||
|
||||
if result.Err = member.IsValid(); result.Err != nil {
|
||||
storeChannel <- result
|
||||
return
|
||||
}
|
||||
|
||||
if err := s.GetMaster().Insert(member); err != nil {
|
||||
if strings.Contains(err.Error(), "Duplicate entry") && strings.Contains(err.Error(), "for key 'ChannelId'") {
|
||||
result.Err = model.NewAppError("SqlChannelStore.SaveMember", "A channel member with that id already exists", "channel_id="+member.ChannelId+", user_id="+member.UserId+", "+err.Error())
|
||||
} else {
|
||||
result.Err = model.NewAppError("SqlChannelStore.SaveMember", "We couldn't save the channel member", "channel_id="+member.ChannelId+", user_id="+member.UserId+", "+err.Error())
|
||||
}
|
||||
} else {
|
||||
result.Data = member
|
||||
}
|
||||
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
}()
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
|
||||
func (s SqlChannelStore) GetMembers(channelId string) StoreChannel {
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
result := StoreResult{}
|
||||
|
||||
var members []model.ChannelMember
|
||||
_, err := s.GetReplica().Select(&members, "SELECT * FROM ChannelMembers WHERE ChannelId = ?", channelId)
|
||||
if err != nil {
|
||||
result.Err = model.NewAppError("SqlChannelStore.GetMembers", "We couldn't get the channel members", "channel_id="+channelId+err.Error())
|
||||
} else {
|
||||
result.Data = members
|
||||
}
|
||||
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
}()
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
|
||||
func (s SqlChannelStore) GetMember(channelId string, userId string) StoreChannel {
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
result := StoreResult{}
|
||||
|
||||
var member model.ChannelMember
|
||||
err := s.GetReplica().SelectOne(&member, "SELECT * FROM ChannelMembers WHERE ChannelId = ? AND UserId = ?", channelId, userId)
|
||||
if err != nil {
|
||||
result.Err = model.NewAppError("SqlChannelStore.GetMember", "We couldn't get the channel member", "channel_id="+channelId+"user_id="+userId+","+err.Error())
|
||||
} else {
|
||||
result.Data = member
|
||||
}
|
||||
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
}()
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
|
||||
func (s SqlChannelStore) GetExtraMembers(channelId string, limit int) StoreChannel {
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
result := StoreResult{}
|
||||
|
||||
var members []model.ExtraMember
|
||||
_, err := s.GetReplica().Select(&members, "SELECT Id, FullName, Email, ChannelMembers.Roles, Username FROM ChannelMembers, Users WHERE ChannelMembers.UserId = Users.Id AND ChannelId = ? LIMIT ?", channelId, limit)
|
||||
if err != nil {
|
||||
result.Err = model.NewAppError("SqlChannelStore.GetExtraMembers", "We couldn't get the extra info for channel members", "channel_id="+channelId+", "+err.Error())
|
||||
} else {
|
||||
for i, _ := range members {
|
||||
members[i].Sanitize(utils.SanitizeOptions)
|
||||
}
|
||||
result.Data = members
|
||||
}
|
||||
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
}()
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
|
||||
func (s SqlChannelStore) RemoveMember(channelId string, userId string) StoreChannel {
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
result := StoreResult{}
|
||||
|
||||
_, err := s.GetMaster().Exec("DELETE FROM ChannelMembers WHERE ChannelId = ? AND UserId = ?", channelId, userId)
|
||||
if err != nil {
|
||||
result.Err = model.NewAppError("SqlChannelStore.RemoveMember", "We couldn't remove the channel member", "channel_id="+channelId+", user_id="+userId+", "+err.Error())
|
||||
}
|
||||
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
}()
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
|
||||
func (s SqlChannelStore) CheckPermissionsTo(teamId string, channelId string, userId string) StoreChannel {
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
result := StoreResult{}
|
||||
|
||||
count, err := s.GetReplica().SelectInt(
|
||||
`SELECT
|
||||
COUNT(0)
|
||||
FROM
|
||||
Channels,
|
||||
ChannelMembers
|
||||
WHERE
|
||||
Channels.Id = ChannelMembers.ChannelId
|
||||
AND Channels.TeamId = ?
|
||||
AND Channels.DeleteAt = 0
|
||||
AND ChannelMembers.ChannelId = ?
|
||||
AND ChannelMembers.UserId = ?`,
|
||||
teamId, channelId, userId)
|
||||
if err != nil {
|
||||
result.Err = model.NewAppError("SqlChannelStore.CheckPermissionsTo", "We couldn't check the permissions", "channel_id="+channelId+", user_id="+userId+", "+err.Error())
|
||||
} else {
|
||||
result.Data = count
|
||||
}
|
||||
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
}()
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
|
||||
func (s SqlChannelStore) CheckPermissionsToByName(teamId string, channelName string, userId string) StoreChannel {
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
result := StoreResult{}
|
||||
|
||||
channelId, err := s.GetReplica().SelectStr(
|
||||
`SELECT
|
||||
Channels.Id
|
||||
FROM
|
||||
Channels,
|
||||
ChannelMembers
|
||||
WHERE
|
||||
Channels.Id = ChannelMembers.ChannelId
|
||||
AND Channels.TeamId = ?
|
||||
AND Channels.Name = ?
|
||||
AND Channels.DeleteAt = 0
|
||||
AND ChannelMembers.UserId = ?`,
|
||||
teamId, channelName, userId)
|
||||
if err != nil {
|
||||
result.Err = model.NewAppError("SqlChannelStore.CheckPermissionsToByName", "We couldn't check the permissions", "channel_id="+channelName+", user_id="+userId+", "+err.Error())
|
||||
} else {
|
||||
result.Data = channelId
|
||||
}
|
||||
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
}()
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
|
||||
func (s SqlChannelStore) CheckOpenChannelPermissions(teamId string, channelId string) StoreChannel {
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
result := StoreResult{}
|
||||
|
||||
count, err := s.GetReplica().SelectInt(
|
||||
`SELECT
|
||||
COUNT(0)
|
||||
FROM
|
||||
Channels
|
||||
WHERE
|
||||
Channels.Id = ?
|
||||
AND Channels.TeamId = ?
|
||||
AND Channels.Type = ?`,
|
||||
channelId, teamId, model.CHANNEL_OPEN)
|
||||
if err != nil {
|
||||
result.Err = model.NewAppError("SqlChannelStore.CheckOpenChannelPermissions", "We couldn't check the permissions", "channel_id="+channelId+", "+err.Error())
|
||||
} else {
|
||||
result.Data = count
|
||||
}
|
||||
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
}()
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
|
||||
func (s SqlChannelStore) UpdateLastViewedAt(channelId string, userId string) StoreChannel {
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
result := StoreResult{}
|
||||
|
||||
_, err := s.GetMaster().Exec(
|
||||
`UPDATE
|
||||
ChannelMembers, Channels
|
||||
SET
|
||||
ChannelMembers.MentionCount = 0,
|
||||
ChannelMembers.MsgCount = Channels.TotalMsgCount,
|
||||
ChannelMembers.LastViewedAt = Channels.LastPostAt
|
||||
WHERE
|
||||
Channels.Id = ChannelMembers.ChannelId
|
||||
AND UserId = ?
|
||||
AND ChannelId = ?`,
|
||||
userId, channelId)
|
||||
if err != nil {
|
||||
result.Err = model.NewAppError("SqlChannelStore.UpdateLastViewedAt", "We couldn't update the last viewed at time", "channel_id="+channelId+", user_id="+userId+", "+err.Error())
|
||||
}
|
||||
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
}()
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
|
||||
func (s SqlChannelStore) IncrementMentionCount(channelId string, userId string) StoreChannel {
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
result := StoreResult{}
|
||||
|
||||
_, err := s.GetMaster().Exec(
|
||||
`UPDATE
|
||||
ChannelMembers
|
||||
SET
|
||||
MentionCount = MentionCount + 1
|
||||
WHERE
|
||||
UserId = ?
|
||||
AND ChannelId = ?`,
|
||||
userId, channelId)
|
||||
if err != nil {
|
||||
result.Err = model.NewAppError("SqlChannelStore.IncrementMentionCount", "We couldn't increment the mention count", "channel_id="+channelId+", user_id="+userId+", "+err.Error())
|
||||
}
|
||||
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
}()
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
|
||||
func (s SqlChannelStore) UpdateNotifyLevel(channelId, userId, notifyLevel string) StoreChannel {
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
result := StoreResult{}
|
||||
|
||||
_, err := s.GetMaster().Exec(
|
||||
`UPDATE
|
||||
ChannelMembers
|
||||
SET
|
||||
NotifyLevel = ?
|
||||
WHERE
|
||||
UserId = ?
|
||||
AND ChannelId = ?`,
|
||||
notifyLevel, userId, channelId)
|
||||
if err != nil {
|
||||
result.Err = model.NewAppError("SqlChannelStore.UpdateNotifyLevel", "We couldn't update the notify level", "channel_id="+channelId+", user_id="+userId+", "+err.Error())
|
||||
}
|
||||
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
}()
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
493
store/sql_channel_store_test.go
Обычный файл
493
store/sql_channel_store_test.go
Обычный файл
@@ -0,0 +1,493 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package store
|
||||
|
||||
import (
|
||||
"github.com/mattermost/platform/model"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestChannelStoreSave(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
teamId := model.NewId()
|
||||
|
||||
o1 := model.Channel{}
|
||||
o1.TeamId = teamId
|
||||
o1.DisplayName = "Name"
|
||||
o1.Name = "a" + model.NewId() + "b"
|
||||
o1.Type = model.CHANNEL_OPEN
|
||||
|
||||
if err := (<-store.Channel().Save(&o1)).Err; err != nil {
|
||||
t.Fatal("couldn't save item", err)
|
||||
}
|
||||
|
||||
if err := (<-store.Channel().Save(&o1)).Err; err == nil {
|
||||
t.Fatal("shouldn't be able to update from save")
|
||||
}
|
||||
|
||||
o1.Id = ""
|
||||
if err := (<-store.Channel().Save(&o1)).Err; err == nil {
|
||||
t.Fatal("should be unique name")
|
||||
}
|
||||
|
||||
for i := 0; i < 150; i++ {
|
||||
o1.Id = ""
|
||||
o1.Name = "a" + model.NewId() + "b"
|
||||
if err := (<-store.Channel().Save(&o1)).Err; err != nil {
|
||||
t.Fatal("couldn't save item", err)
|
||||
}
|
||||
}
|
||||
|
||||
o1.Id = ""
|
||||
o1.Name = "a" + model.NewId() + "b"
|
||||
if err := (<-store.Channel().Save(&o1)).Err; err == nil {
|
||||
t.Fatal("should be the limit")
|
||||
}
|
||||
}
|
||||
|
||||
func TestChannelStoreUpdate(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
o1 := model.Channel{}
|
||||
o1.TeamId = model.NewId()
|
||||
o1.DisplayName = "Name"
|
||||
o1.Name = "a" + model.NewId() + "b"
|
||||
o1.Type = model.CHANNEL_OPEN
|
||||
<-store.Channel().Save(&o1)
|
||||
|
||||
if err := (<-store.Channel().Update(&o1)).Err; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
o1.Id = "missing"
|
||||
if err := (<-store.Channel().Update(&o1)).Err; err == nil {
|
||||
t.Fatal("Update should have failed because of missing key")
|
||||
}
|
||||
|
||||
o1.Id = model.NewId()
|
||||
if err := (<-store.Channel().Update(&o1)).Err; err == nil {
|
||||
t.Fatal("Update should have faile because id change")
|
||||
}
|
||||
}
|
||||
|
||||
func TestChannelStoreGet(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
o1 := model.Channel{}
|
||||
o1.TeamId = model.NewId()
|
||||
o1.DisplayName = "Name"
|
||||
o1.Name = "a" + model.NewId() + "b"
|
||||
o1.Type = model.CHANNEL_OPEN
|
||||
<-store.Channel().Save(&o1)
|
||||
|
||||
if r1 := <-store.Channel().Get(o1.Id); r1.Err != nil {
|
||||
t.Fatal(r1.Err)
|
||||
} else {
|
||||
if r1.Data.(*model.Channel).ToJson() != o1.ToJson() {
|
||||
t.Fatal("invalid returned channel")
|
||||
}
|
||||
}
|
||||
|
||||
if err := (<-store.Channel().Get("")).Err; err == nil {
|
||||
t.Fatal("Missing id should have failed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestChannelStoreDelete(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
o1 := model.Channel{}
|
||||
o1.TeamId = model.NewId()
|
||||
o1.DisplayName = "Channel1"
|
||||
o1.Name = "a" + model.NewId() + "b"
|
||||
o1.Type = model.CHANNEL_OPEN
|
||||
<-store.Channel().Save(&o1)
|
||||
|
||||
o2 := model.Channel{}
|
||||
o2.TeamId = o1.TeamId
|
||||
o2.DisplayName = "Channel2"
|
||||
o2.Name = "a" + model.NewId() + "b"
|
||||
o2.Type = model.CHANNEL_OPEN
|
||||
<-store.Channel().Save(&o2)
|
||||
|
||||
o3 := model.Channel{}
|
||||
o3.TeamId = o1.TeamId
|
||||
o3.DisplayName = "Channel3"
|
||||
o3.Name = "a" + model.NewId() + "b"
|
||||
o3.Type = model.CHANNEL_OPEN
|
||||
<-store.Channel().Save(&o3)
|
||||
|
||||
o4 := model.Channel{}
|
||||
o4.TeamId = o1.TeamId
|
||||
o4.DisplayName = "Channel4"
|
||||
o4.Name = "a" + model.NewId() + "b"
|
||||
o4.Type = model.CHANNEL_OPEN
|
||||
<-store.Channel().Save(&o4)
|
||||
|
||||
m1 := model.ChannelMember{}
|
||||
m1.ChannelId = o1.Id
|
||||
m1.UserId = model.NewId()
|
||||
m1.NotifyLevel = model.CHANNEL_NOTIFY_ALL
|
||||
<-store.Channel().SaveMember(&m1)
|
||||
|
||||
m2 := model.ChannelMember{}
|
||||
m2.ChannelId = o2.Id
|
||||
m2.UserId = m1.UserId
|
||||
m2.NotifyLevel = model.CHANNEL_NOTIFY_ALL
|
||||
<-store.Channel().SaveMember(&m2)
|
||||
|
||||
if r := <-store.Channel().Delete(o1.Id, model.GetMillis()); r.Err != nil {
|
||||
t.Fatal(r.Err)
|
||||
}
|
||||
|
||||
if r := <-store.Channel().Get(o1.Id); r.Data.(*model.Channel).DeleteAt == 0 {
|
||||
t.Fatal("should have been deleted")
|
||||
}
|
||||
|
||||
if r := <-store.Channel().Delete(o3.Id, model.GetMillis()); r.Err != nil {
|
||||
t.Fatal(r.Err)
|
||||
}
|
||||
|
||||
cresult := <-store.Channel().GetChannels(o1.TeamId, m1.UserId)
|
||||
list := cresult.Data.(*model.ChannelList)
|
||||
|
||||
if len(list.Channels) != 1 {
|
||||
t.Fatal("invalid number of channels")
|
||||
}
|
||||
|
||||
cresult = <-store.Channel().GetMoreChannels(o1.TeamId, m1.UserId)
|
||||
list = cresult.Data.(*model.ChannelList)
|
||||
|
||||
if len(list.Channels) != 1 {
|
||||
t.Fatal("invalid number of channels")
|
||||
}
|
||||
}
|
||||
|
||||
func TestChannelStoreGetByName(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
o1 := model.Channel{}
|
||||
o1.TeamId = model.NewId()
|
||||
o1.DisplayName = "Name"
|
||||
o1.Name = "a" + model.NewId() + "b"
|
||||
o1.Type = model.CHANNEL_OPEN
|
||||
<-store.Channel().Save(&o1)
|
||||
|
||||
if r1 := <-store.Channel().GetByName(o1.TeamId, o1.Name); r1.Err != nil {
|
||||
t.Fatal(r1.Err)
|
||||
} else {
|
||||
if r1.Data.(*model.Channel).ToJson() != o1.ToJson() {
|
||||
t.Fatal("invalid returned channel")
|
||||
}
|
||||
}
|
||||
|
||||
if err := (<-store.Channel().GetByName(o1.TeamId, "")).Err; err == nil {
|
||||
t.Fatal("Missing id should have failed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestChannelMemberStore(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
u1 := model.User{}
|
||||
u1.TeamId = model.NewId()
|
||||
u1.Email = model.NewId()
|
||||
u1.FullName = model.NewId()
|
||||
<-store.User().Save(&u1)
|
||||
|
||||
u2 := model.User{}
|
||||
u2.TeamId = model.NewId()
|
||||
u2.Email = model.NewId()
|
||||
u2.FullName = model.NewId()
|
||||
<-store.User().Save(&u2)
|
||||
|
||||
o1 := model.ChannelMember{}
|
||||
o1.ChannelId = model.NewId()
|
||||
o1.UserId = u1.Id
|
||||
o1.NotifyLevel = model.CHANNEL_NOTIFY_ALL
|
||||
<-store.Channel().SaveMember(&o1)
|
||||
|
||||
o2 := model.ChannelMember{}
|
||||
o2.ChannelId = o1.ChannelId
|
||||
o2.UserId = u2.Id
|
||||
o2.NotifyLevel = model.CHANNEL_NOTIFY_ALL
|
||||
<-store.Channel().SaveMember(&o2)
|
||||
|
||||
members := (<-store.Channel().GetMembers(o1.ChannelId)).Data.([]model.ChannelMember)
|
||||
if len(members) != 2 {
|
||||
t.Fatal("should have saved 2 members")
|
||||
}
|
||||
|
||||
<-store.Channel().RemoveMember(o2.ChannelId, o2.UserId)
|
||||
|
||||
members = (<-store.Channel().GetMembers(o1.ChannelId)).Data.([]model.ChannelMember)
|
||||
if len(members) != 1 {
|
||||
t.Fatal("should have removed 1 member")
|
||||
}
|
||||
|
||||
member := (<-store.Channel().GetMember(o1.ChannelId, o1.UserId)).Data.(model.ChannelMember)
|
||||
if member.ChannelId != o1.ChannelId {
|
||||
t.Fatal("should have go member")
|
||||
}
|
||||
|
||||
extraMembers := (<-store.Channel().GetExtraMembers(o1.ChannelId, 20)).Data.([]model.ExtraMember)
|
||||
if len(extraMembers) != 1 {
|
||||
t.Fatal("should have 1 extra members")
|
||||
}
|
||||
|
||||
if err := (<-store.Channel().SaveMember(&o1)).Err; err == nil {
|
||||
t.Fatal("Should have been a duplicate")
|
||||
}
|
||||
}
|
||||
|
||||
func TestChannelStorePermissionsTo(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
o1 := model.Channel{}
|
||||
o1.TeamId = model.NewId()
|
||||
o1.DisplayName = "Channel1"
|
||||
o1.Name = "a" + model.NewId() + "b"
|
||||
o1.Type = model.CHANNEL_OPEN
|
||||
<-store.Channel().Save(&o1)
|
||||
|
||||
m1 := model.ChannelMember{}
|
||||
m1.ChannelId = o1.Id
|
||||
m1.UserId = model.NewId()
|
||||
m1.NotifyLevel = model.CHANNEL_NOTIFY_ALL
|
||||
<-store.Channel().SaveMember(&m1)
|
||||
|
||||
count := (<-store.Channel().CheckPermissionsTo(o1.TeamId, o1.Id, m1.UserId)).Data.(int64)
|
||||
if count != 1 {
|
||||
t.Fatal("should have permissions")
|
||||
}
|
||||
|
||||
count = (<-store.Channel().CheckPermissionsTo("junk", o1.Id, m1.UserId)).Data.(int64)
|
||||
if count != 0 {
|
||||
t.Fatal("shouldn't have permissions")
|
||||
}
|
||||
|
||||
count = (<-store.Channel().CheckPermissionsTo(o1.TeamId, "junk", m1.UserId)).Data.(int64)
|
||||
if count != 0 {
|
||||
t.Fatal("shouldn't have permissions")
|
||||
}
|
||||
|
||||
count = (<-store.Channel().CheckPermissionsTo(o1.TeamId, o1.Id, "junk")).Data.(int64)
|
||||
if count != 0 {
|
||||
t.Fatal("shouldn't have permissions")
|
||||
}
|
||||
|
||||
channelId := (<-store.Channel().CheckPermissionsToByName(o1.TeamId, o1.Name, m1.UserId)).Data.(string)
|
||||
if channelId != o1.Id {
|
||||
t.Fatal("should have permissions")
|
||||
}
|
||||
|
||||
channelId = (<-store.Channel().CheckPermissionsToByName(o1.TeamId, "missing", m1.UserId)).Data.(string)
|
||||
if channelId != "" {
|
||||
t.Fatal("should not have permissions")
|
||||
}
|
||||
}
|
||||
|
||||
func TestChannelStoreOpenChannelPermissionsTo(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
o1 := model.Channel{}
|
||||
o1.TeamId = model.NewId()
|
||||
o1.DisplayName = "Channel1"
|
||||
o1.Name = "a" + model.NewId() + "b"
|
||||
o1.Type = model.CHANNEL_OPEN
|
||||
<-store.Channel().Save(&o1)
|
||||
|
||||
count := (<-store.Channel().CheckOpenChannelPermissions(o1.TeamId, o1.Id)).Data.(int64)
|
||||
if count != 1 {
|
||||
t.Fatal("should have permissions")
|
||||
}
|
||||
|
||||
count = (<-store.Channel().CheckOpenChannelPermissions("junk", o1.Id)).Data.(int64)
|
||||
if count != 0 {
|
||||
t.Fatal("shouldn't have permissions")
|
||||
}
|
||||
|
||||
count = (<-store.Channel().CheckOpenChannelPermissions(o1.TeamId, "junk")).Data.(int64)
|
||||
if count != 0 {
|
||||
t.Fatal("shouldn't have permissions")
|
||||
}
|
||||
}
|
||||
|
||||
func TestChannelStoreGetChannels(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
o2 := model.Channel{}
|
||||
o2.TeamId = model.NewId()
|
||||
o2.DisplayName = "Channel2"
|
||||
o2.Name = "a" + model.NewId() + "b"
|
||||
o2.Type = model.CHANNEL_OPEN
|
||||
<-store.Channel().Save(&o2)
|
||||
|
||||
o1 := model.Channel{}
|
||||
o1.TeamId = model.NewId()
|
||||
o1.DisplayName = "Channel1"
|
||||
o1.Name = "a" + model.NewId() + "b"
|
||||
o1.Type = model.CHANNEL_OPEN
|
||||
<-store.Channel().Save(&o1)
|
||||
|
||||
m1 := model.ChannelMember{}
|
||||
m1.ChannelId = o1.Id
|
||||
m1.UserId = model.NewId()
|
||||
m1.NotifyLevel = model.CHANNEL_NOTIFY_ALL
|
||||
<-store.Channel().SaveMember(&m1)
|
||||
|
||||
m2 := model.ChannelMember{}
|
||||
m2.ChannelId = o1.Id
|
||||
m2.UserId = model.NewId()
|
||||
m2.NotifyLevel = model.CHANNEL_NOTIFY_ALL
|
||||
<-store.Channel().SaveMember(&m2)
|
||||
|
||||
m3 := model.ChannelMember{}
|
||||
m3.ChannelId = o2.Id
|
||||
m3.UserId = model.NewId()
|
||||
m3.NotifyLevel = model.CHANNEL_NOTIFY_ALL
|
||||
<-store.Channel().SaveMember(&m3)
|
||||
|
||||
cresult := <-store.Channel().GetChannels(o1.TeamId, m1.UserId)
|
||||
list := cresult.Data.(*model.ChannelList)
|
||||
|
||||
if list.Channels[0].Id != o1.Id {
|
||||
t.Fatal("missing channel")
|
||||
}
|
||||
}
|
||||
|
||||
func TestChannelStoreGetMoreChannels(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
o1 := model.Channel{}
|
||||
o1.TeamId = model.NewId()
|
||||
o1.DisplayName = "Channel1"
|
||||
o1.Name = "a" + model.NewId() + "b"
|
||||
o1.Type = model.CHANNEL_OPEN
|
||||
<-store.Channel().Save(&o1)
|
||||
|
||||
o2 := model.Channel{}
|
||||
o2.TeamId = model.NewId()
|
||||
o2.DisplayName = "Channel2"
|
||||
o2.Name = "a" + model.NewId() + "b"
|
||||
o2.Type = model.CHANNEL_OPEN
|
||||
<-store.Channel().Save(&o2)
|
||||
|
||||
m1 := model.ChannelMember{}
|
||||
m1.ChannelId = o1.Id
|
||||
m1.UserId = model.NewId()
|
||||
m1.NotifyLevel = model.CHANNEL_NOTIFY_ALL
|
||||
<-store.Channel().SaveMember(&m1)
|
||||
|
||||
m2 := model.ChannelMember{}
|
||||
m2.ChannelId = o1.Id
|
||||
m2.UserId = model.NewId()
|
||||
m2.NotifyLevel = model.CHANNEL_NOTIFY_ALL
|
||||
<-store.Channel().SaveMember(&m2)
|
||||
|
||||
m3 := model.ChannelMember{}
|
||||
m3.ChannelId = o2.Id
|
||||
m3.UserId = model.NewId()
|
||||
m3.NotifyLevel = model.CHANNEL_NOTIFY_ALL
|
||||
<-store.Channel().SaveMember(&m3)
|
||||
|
||||
o3 := model.Channel{}
|
||||
o3.TeamId = o1.TeamId
|
||||
o3.DisplayName = "ChannelA"
|
||||
o3.Name = "a" + model.NewId() + "b"
|
||||
o3.Type = model.CHANNEL_OPEN
|
||||
<-store.Channel().Save(&o3)
|
||||
|
||||
o4 := model.Channel{}
|
||||
o4.TeamId = o1.TeamId
|
||||
o4.DisplayName = "ChannelB"
|
||||
o4.Name = "a" + model.NewId() + "b"
|
||||
o4.Type = model.CHANNEL_PRIVATE
|
||||
<-store.Channel().Save(&o4)
|
||||
|
||||
o5 := model.Channel{}
|
||||
o5.TeamId = o1.TeamId
|
||||
o5.DisplayName = "ChannelC"
|
||||
o5.Name = "a" + model.NewId() + "b"
|
||||
o5.Type = model.CHANNEL_PRIVATE
|
||||
<-store.Channel().Save(&o5)
|
||||
|
||||
cresult := <-store.Channel().GetMoreChannels(o1.TeamId, m1.UserId)
|
||||
list := cresult.Data.(*model.ChannelList)
|
||||
|
||||
if len(list.Channels) != 1 {
|
||||
t.Fatal("wrong list")
|
||||
}
|
||||
|
||||
if list.Channels[0].Name != o3.Name {
|
||||
t.Fatal("missing channel")
|
||||
}
|
||||
}
|
||||
|
||||
func TestChannelStoreUpdateLastViewedAt(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
o1 := model.Channel{}
|
||||
o1.TeamId = model.NewId()
|
||||
o1.DisplayName = "Channel1"
|
||||
o1.Name = "a" + model.NewId() + "b"
|
||||
o1.Type = model.CHANNEL_OPEN
|
||||
o1.TotalMsgCount = 25
|
||||
<-store.Channel().Save(&o1)
|
||||
|
||||
m1 := model.ChannelMember{}
|
||||
m1.ChannelId = o1.Id
|
||||
m1.UserId = model.NewId()
|
||||
m1.NotifyLevel = model.CHANNEL_NOTIFY_ALL
|
||||
<-store.Channel().SaveMember(&m1)
|
||||
|
||||
err := (<-store.Channel().UpdateLastViewedAt(m1.ChannelId, m1.UserId)).Err
|
||||
if err != nil {
|
||||
t.Fatal("failed to update")
|
||||
}
|
||||
|
||||
err = (<-store.Channel().UpdateLastViewedAt(m1.ChannelId, "missing id")).Err
|
||||
if err != nil {
|
||||
t.Fatal("failed to update")
|
||||
}
|
||||
}
|
||||
|
||||
func TestChannelStoreIncrementMentionCount(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
o1 := model.Channel{}
|
||||
o1.TeamId = model.NewId()
|
||||
o1.DisplayName = "Channel1"
|
||||
o1.Name = "a" + model.NewId() + "b"
|
||||
o1.Type = model.CHANNEL_OPEN
|
||||
o1.TotalMsgCount = 25
|
||||
<-store.Channel().Save(&o1)
|
||||
|
||||
m1 := model.ChannelMember{}
|
||||
m1.ChannelId = o1.Id
|
||||
m1.UserId = model.NewId()
|
||||
m1.NotifyLevel = model.CHANNEL_NOTIFY_ALL
|
||||
<-store.Channel().SaveMember(&m1)
|
||||
|
||||
err := (<-store.Channel().IncrementMentionCount(m1.ChannelId, m1.UserId)).Err
|
||||
if err != nil {
|
||||
t.Fatal("failed to update")
|
||||
}
|
||||
|
||||
err = (<-store.Channel().IncrementMentionCount(m1.ChannelId, "missing id")).Err
|
||||
if err != nil {
|
||||
t.Fatal("failed to update")
|
||||
}
|
||||
|
||||
err = (<-store.Channel().IncrementMentionCount("missing id", m1.UserId)).Err
|
||||
if err != nil {
|
||||
t.Fatal("failed to update")
|
||||
}
|
||||
|
||||
err = (<-store.Channel().IncrementMentionCount("missing id", "missing id")).Err
|
||||
if err != nil {
|
||||
t.Fatal("failed to update")
|
||||
}
|
||||
}
|
||||
410
store/sql_post_store.go
Обычный файл
410
store/sql_post_store.go
Обычный файл
@@ -0,0 +1,410 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package store
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/mattermost/platform/model"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type SqlPostStore struct {
|
||||
*SqlStore
|
||||
}
|
||||
|
||||
func NewSqlPostStore(sqlStore *SqlStore) PostStore {
|
||||
s := &SqlPostStore{sqlStore}
|
||||
|
||||
for _, db := range sqlStore.GetAllConns() {
|
||||
table := db.AddTableWithName(model.Post{}, "Posts").SetKeys(false, "Id")
|
||||
table.ColMap("Id").SetMaxSize(26)
|
||||
table.ColMap("UserId").SetMaxSize(26)
|
||||
table.ColMap("ChannelId").SetMaxSize(26)
|
||||
table.ColMap("RootId").SetMaxSize(26)
|
||||
table.ColMap("ParentId").SetMaxSize(26)
|
||||
table.ColMap("Message").SetMaxSize(4000)
|
||||
table.ColMap("Type").SetMaxSize(26)
|
||||
table.ColMap("Hashtags").SetMaxSize(1000)
|
||||
table.ColMap("Props").SetMaxSize(4000)
|
||||
table.ColMap("Filenames").SetMaxSize(4000)
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func (s SqlPostStore) UpgradeSchemaIfNeeded() {
|
||||
}
|
||||
|
||||
func (s SqlPostStore) CreateIndexesIfNotExists() {
|
||||
s.CreateIndexIfNotExists("idx_update_at", "Posts", "UpdateAt")
|
||||
s.CreateIndexIfNotExists("idx_create_at", "Posts", "CreateAt")
|
||||
s.CreateIndexIfNotExists("idx_channel_id", "Posts", "ChannelId")
|
||||
s.CreateIndexIfNotExists("idx_root_id", "Posts", "RootId")
|
||||
|
||||
s.CreateFullTextIndexIfNotExists("idx_message_txt", "Posts", "Message")
|
||||
s.CreateFullTextIndexIfNotExists("idx_hashtags_txt", "Posts", "Hashtags")
|
||||
}
|
||||
|
||||
func (s SqlPostStore) Save(post *model.Post) StoreChannel {
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
result := StoreResult{}
|
||||
|
||||
if len(post.Id) > 0 {
|
||||
result.Err = model.NewAppError("SqlPostStore.Save",
|
||||
"You cannot update an existing Post", "id="+post.Id)
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
return
|
||||
}
|
||||
|
||||
post.PreSave()
|
||||
if result.Err = post.IsValid(); result.Err != nil {
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
return
|
||||
}
|
||||
|
||||
if err := s.GetMaster().Insert(post); err != nil {
|
||||
result.Err = model.NewAppError("SqlPostStore.Save", "We couldn't save the Post", "id="+post.Id+", "+err.Error())
|
||||
} else {
|
||||
time := model.GetMillis()
|
||||
s.GetMaster().Exec("UPDATE Channels SET LastPostAt = ?, TotalMsgCount = TotalMsgCount + 1 WHERE Id = ?", time, post.ChannelId)
|
||||
|
||||
if len(post.RootId) > 0 {
|
||||
s.GetMaster().Exec("UPDATE Posts SET UpdateAt = ? WHERE Id = ?", time, post.RootId)
|
||||
}
|
||||
|
||||
result.Data = post
|
||||
}
|
||||
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
}()
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
|
||||
func (s SqlPostStore) Update(oldPost *model.Post, newMessage string, newHashtags string) StoreChannel {
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
result := StoreResult{}
|
||||
|
||||
editPost := *oldPost
|
||||
editPost.Message = newMessage
|
||||
editPost.UpdateAt = model.GetMillis()
|
||||
editPost.Hashtags = newHashtags
|
||||
|
||||
oldPost.DeleteAt = editPost.UpdateAt
|
||||
oldPost.UpdateAt = editPost.UpdateAt
|
||||
oldPost.OriginalId = oldPost.Id
|
||||
oldPost.Id = model.NewId()
|
||||
|
||||
if result.Err = editPost.IsValid(); result.Err != nil {
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := s.GetMaster().Update(&editPost); err != nil {
|
||||
result.Err = model.NewAppError("SqlPostStore.Update", "We couldn't update the Post", "id="+editPost.Id+", "+err.Error())
|
||||
} else {
|
||||
time := model.GetMillis()
|
||||
s.GetMaster().Exec("UPDATE Channels SET LastPostAt = ? WHERE Id = ?", time, editPost.ChannelId)
|
||||
|
||||
if len(editPost.RootId) > 0 {
|
||||
s.GetMaster().Exec("UPDATE Posts SET UpdateAt = ? WHERE Id = ?", time, editPost.RootId)
|
||||
}
|
||||
|
||||
// mark the old post as deleted
|
||||
s.GetMaster().Insert(oldPost)
|
||||
|
||||
result.Data = &editPost
|
||||
}
|
||||
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
}()
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
|
||||
func (s SqlPostStore) Get(id string) StoreChannel {
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
result := StoreResult{}
|
||||
pl := &model.PostList{}
|
||||
|
||||
var post model.Post
|
||||
err := s.GetReplica().SelectOne(&post, "SELECT * FROM Posts WHERE Id = ? AND DeleteAt = 0", id)
|
||||
if err != nil {
|
||||
result.Err = model.NewAppError("SqlPostStore.GetPost", "We couldn't get the post", "id="+id+err.Error())
|
||||
}
|
||||
|
||||
if post.ImgCount > 0 {
|
||||
post.Filenames = []string{}
|
||||
for i := 0; int64(i) < post.ImgCount; i++ {
|
||||
fileUrl := "/api/v1/files/get_image/" + post.ChannelId + "/" + post.Id + "/" + strconv.Itoa(i+1) + ".png"
|
||||
post.Filenames = append(post.Filenames, fileUrl)
|
||||
}
|
||||
}
|
||||
|
||||
pl.AddPost(&post)
|
||||
pl.AddOrder(id)
|
||||
|
||||
rootId := post.RootId
|
||||
|
||||
if rootId == "" {
|
||||
rootId = post.Id
|
||||
}
|
||||
|
||||
var posts []*model.Post
|
||||
_, err = s.GetReplica().Select(&posts, "SELECT * FROM Posts WHERE (Id = ? OR RootId = ?) AND DeleteAt = 0", rootId, rootId)
|
||||
if err != nil {
|
||||
result.Err = model.NewAppError("SqlPostStore.GetPost", "We couldn't get the post", "root_id="+rootId+err.Error())
|
||||
} else {
|
||||
for _, p := range posts {
|
||||
pl.AddPost(p)
|
||||
}
|
||||
}
|
||||
|
||||
result.Data = pl
|
||||
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
}()
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
|
||||
type etagPosts struct {
|
||||
Id string
|
||||
UpdateAt int64
|
||||
}
|
||||
|
||||
func (s SqlPostStore) GetEtag(channelId string) StoreChannel {
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
result := StoreResult{}
|
||||
|
||||
var et etagPosts
|
||||
err := s.GetReplica().SelectOne(&et, "SELECT Id, UpdateAt FROM Posts WHERE ChannelId = ? ORDER BY UpdateAt DESC LIMIT 1", channelId)
|
||||
if err != nil {
|
||||
result.Data = fmt.Sprintf("%v.0.%v", model.ETAG_ROOT_VERSION, model.GetMillis())
|
||||
} else {
|
||||
result.Data = fmt.Sprintf("%v.%v.%v", model.ETAG_ROOT_VERSION, et.Id, et.UpdateAt)
|
||||
}
|
||||
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
}()
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
|
||||
func (s SqlPostStore) Delete(postId string, time int64) StoreChannel {
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
result := StoreResult{}
|
||||
|
||||
_, err := s.GetMaster().Exec("Update Posts SET DeleteAt = ?, UpdateAt = ? WHERE Id = ? OR ParentId = ? OR RootId = ?", time, time, postId, postId, postId)
|
||||
if err != nil {
|
||||
result.Err = model.NewAppError("SqlPostStore.Delete", "We couldn't delete the post", "id="+postId+", err="+err.Error())
|
||||
}
|
||||
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
}()
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
|
||||
func (s SqlPostStore) GetPosts(channelId string, offset int, limit int) StoreChannel {
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
result := StoreResult{}
|
||||
|
||||
if limit > 1000 {
|
||||
result.Err = model.NewAppError("SqlPostStore.GetLinearPosts", "Limit exceeded for paging", "channelId="+channelId)
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
return
|
||||
}
|
||||
|
||||
rpc := s.getRootPosts(channelId, offset, limit)
|
||||
cpc := s.getParentsPosts(channelId, offset, limit)
|
||||
|
||||
if rpr := <-rpc; rpr.Err != nil {
|
||||
result.Err = rpr.Err
|
||||
} else if cpr := <-cpc; cpr.Err != nil {
|
||||
result.Err = cpr.Err
|
||||
} else {
|
||||
posts := rpr.Data.([]*model.Post)
|
||||
parents := cpr.Data.([]*model.Post)
|
||||
|
||||
list := &model.PostList{Order: make([]string, 0, len(posts))}
|
||||
|
||||
for _, p := range posts {
|
||||
if p.ImgCount > 0 {
|
||||
p.Filenames = []string{}
|
||||
for i := 0; int64(i) < p.ImgCount; i++ {
|
||||
fileUrl := "/api/v1/files/get_image/" + p.ChannelId + "/" + p.Id + "/" + strconv.Itoa(i+1) + ".png"
|
||||
p.Filenames = append(p.Filenames, fileUrl)
|
||||
}
|
||||
}
|
||||
list.AddPost(p)
|
||||
list.AddOrder(p.Id)
|
||||
}
|
||||
|
||||
for _, p := range parents {
|
||||
if p.ImgCount > 0 {
|
||||
p.Filenames = []string{}
|
||||
for i := 0; int64(i) < p.ImgCount; i++ {
|
||||
fileUrl := "/api/v1/files/get_image/" + p.ChannelId + "/" + p.Id + "/" + strconv.Itoa(i+1) + ".png"
|
||||
p.Filenames = append(p.Filenames, fileUrl)
|
||||
}
|
||||
}
|
||||
list.AddPost(p)
|
||||
}
|
||||
|
||||
list.MakeNonNil()
|
||||
|
||||
result.Data = list
|
||||
}
|
||||
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
}()
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
|
||||
func (s SqlPostStore) getRootPosts(channelId string, offset int, limit int) StoreChannel {
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
result := StoreResult{}
|
||||
|
||||
var posts []*model.Post
|
||||
_, err := s.GetReplica().Select(&posts, "SELECT * FROM Posts WHERE ChannelId = ? AND DeleteAt = 0 ORDER BY CreateAt DESC LIMIT ?,?", channelId, offset, limit)
|
||||
if err != nil {
|
||||
result.Err = model.NewAppError("SqlPostStore.GetLinearPosts", "We couldn't get the posts for the channel", "channelId="+channelId+err.Error())
|
||||
} else {
|
||||
result.Data = posts
|
||||
}
|
||||
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
}()
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
|
||||
func (s SqlPostStore) getParentsPosts(channelId string, offset int, limit int) StoreChannel {
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
result := StoreResult{}
|
||||
|
||||
var posts []*model.Post
|
||||
_, err := s.GetReplica().Select(&posts,
|
||||
`SELECT
|
||||
q2.*
|
||||
FROM
|
||||
Posts q2
|
||||
INNER JOIN
|
||||
(SELECT DISTINCT
|
||||
q3.RootId
|
||||
FROM
|
||||
(SELECT
|
||||
RootId
|
||||
FROM
|
||||
Posts
|
||||
WHERE
|
||||
ChannelId = ?
|
||||
AND DeleteAt = 0
|
||||
ORDER BY CreateAt DESC
|
||||
LIMIT ?, ?) q3) q1 ON q1.RootId = q2.RootId
|
||||
WHERE
|
||||
ChannelId = ?
|
||||
AND DeleteAt = 0
|
||||
ORDER BY CreateAt`,
|
||||
channelId, offset, limit, channelId)
|
||||
if err != nil {
|
||||
result.Err = model.NewAppError("SqlPostStore.GetLinearPosts", "We couldn't get the parent post for the channel", "channelId="+channelId+err.Error())
|
||||
} else {
|
||||
result.Data = posts
|
||||
}
|
||||
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
}()
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
|
||||
func (s SqlPostStore) Search(teamId string, userId string, terms string, isHashtagSearch bool) StoreChannel {
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
result := StoreResult{}
|
||||
searchType := "Message"
|
||||
if isHashtagSearch {
|
||||
searchType = "Hashtags"
|
||||
}
|
||||
|
||||
// @ has a speical meaning in INNODB FULLTEXT indexes and
|
||||
// is reserved for calc'ing distances so you
|
||||
// cannot escape it so we replace it.
|
||||
terms = strings.Replace(terms, "@", " ", -1)
|
||||
|
||||
searchQuery := fmt.Sprintf(`SELECT
|
||||
*
|
||||
FROM
|
||||
Posts
|
||||
WHERE
|
||||
DeleteAt = 0
|
||||
AND ChannelId IN (SELECT
|
||||
Id
|
||||
FROM
|
||||
Channels,
|
||||
ChannelMembers
|
||||
WHERE
|
||||
Id = ChannelId AND TeamId = ?
|
||||
AND UserId = ?
|
||||
AND DeleteAt = 0)
|
||||
AND MATCH (%s) AGAINST (? IN BOOLEAN MODE)
|
||||
ORDER BY CreateAt DESC
|
||||
LIMIT 100`, searchType)
|
||||
|
||||
var posts []*model.Post
|
||||
_, err := s.GetReplica().Select(&posts, searchQuery, teamId, userId, terms)
|
||||
if err != nil {
|
||||
result.Err = model.NewAppError("SqlPostStore.Search", "We encounted an error while searching for posts", "teamId="+teamId+", err="+err.Error())
|
||||
} else {
|
||||
|
||||
list := &model.PostList{Order: make([]string, 0, len(posts))}
|
||||
|
||||
for _, p := range posts {
|
||||
list.AddPost(p)
|
||||
list.AddOrder(p.Id)
|
||||
}
|
||||
|
||||
list.MakeNonNil()
|
||||
|
||||
result.Data = list
|
||||
}
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
}()
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
481
store/sql_post_store_test.go
Обычный файл
481
store/sql_post_store_test.go
Обычный файл
@@ -0,0 +1,481 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package store
|
||||
|
||||
import (
|
||||
"github.com/mattermost/platform/model"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestPostStoreSave(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
o1 := model.Post{}
|
||||
o1.ChannelId = model.NewId()
|
||||
o1.UserId = model.NewId()
|
||||
o1.Message = "a" + model.NewId() + "b"
|
||||
|
||||
if err := (<-store.Post().Save(&o1)).Err; err != nil {
|
||||
t.Fatal("couldn't save item", err)
|
||||
}
|
||||
|
||||
if err := (<-store.Post().Save(&o1)).Err; err == nil {
|
||||
t.Fatal("shouldn't be able to update from save")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPostStoreGet(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
o1 := &model.Post{}
|
||||
o1.ChannelId = model.NewId()
|
||||
o1.UserId = model.NewId()
|
||||
o1.Message = "a" + model.NewId() + "b"
|
||||
|
||||
etag1 := (<-store.Post().GetEtag(o1.ChannelId)).Data.(string)
|
||||
if strings.Index(etag1, model.ETAG_ROOT_VERSION+".0.") != 0 {
|
||||
t.Fatal("Invalid Etag")
|
||||
}
|
||||
|
||||
o1 = (<-store.Post().Save(o1)).Data.(*model.Post)
|
||||
|
||||
etag2 := (<-store.Post().GetEtag(o1.ChannelId)).Data.(string)
|
||||
if strings.Index(etag2, model.ETAG_ROOT_VERSION+"."+o1.Id) != 0 {
|
||||
t.Fatal("Invalid Etag")
|
||||
}
|
||||
|
||||
if r1 := <-store.Post().Get(o1.Id); r1.Err != nil {
|
||||
t.Fatal(r1.Err)
|
||||
} else {
|
||||
if r1.Data.(*model.PostList).Posts[o1.Id].CreateAt != o1.CreateAt {
|
||||
t.Fatal("invalid returned post")
|
||||
}
|
||||
}
|
||||
|
||||
if err := (<-store.Post().Get("123")).Err; err == nil {
|
||||
t.Fatal("Missing id should have failed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPostStoreUpdate(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
o1 := &model.Post{}
|
||||
o1.ChannelId = model.NewId()
|
||||
o1.UserId = model.NewId()
|
||||
o1.Message = "a" + model.NewId() + "AAAAAAAAAAA"
|
||||
o1 = (<-store.Post().Save(o1)).Data.(*model.Post)
|
||||
|
||||
o2 := &model.Post{}
|
||||
o2.ChannelId = o1.ChannelId
|
||||
o2.UserId = model.NewId()
|
||||
o2.Message = "a" + model.NewId() + "CCCCCCCCC"
|
||||
o2.ParentId = o1.Id
|
||||
o2.RootId = o1.Id
|
||||
o2 = (<-store.Post().Save(o2)).Data.(*model.Post)
|
||||
|
||||
o3 := &model.Post{}
|
||||
o3.ChannelId = o1.ChannelId
|
||||
o3.UserId = model.NewId()
|
||||
o3.Message = "a" + model.NewId() + "QQQQQQQQQQ"
|
||||
o3 = (<-store.Post().Save(o3)).Data.(*model.Post)
|
||||
|
||||
ro1 := (<-store.Post().Get(o1.Id)).Data.(*model.PostList).Posts[o1.Id]
|
||||
ro2 := (<-store.Post().Get(o1.Id)).Data.(*model.PostList).Posts[o2.Id]
|
||||
ro6 := (<-store.Post().Get(o3.Id)).Data.(*model.PostList).Posts[o3.Id]
|
||||
|
||||
if ro1.Message != o1.Message {
|
||||
t.Fatal("Failed to save/get")
|
||||
}
|
||||
|
||||
msg := o1.Message + "BBBBBBBBBB"
|
||||
if result := <-store.Post().Update(ro1, msg, ""); result.Err != nil {
|
||||
t.Fatal(result.Err)
|
||||
}
|
||||
|
||||
msg2 := o2.Message + "DDDDDDD"
|
||||
if result := <-store.Post().Update(ro2, msg2, ""); result.Err != nil {
|
||||
t.Fatal(result.Err)
|
||||
}
|
||||
|
||||
msg3 := o3.Message + "WWWWWWW"
|
||||
if result := <-store.Post().Update(ro6, msg3, "#hashtag"); result.Err != nil {
|
||||
t.Fatal(result.Err)
|
||||
}
|
||||
|
||||
ro3 := (<-store.Post().Get(o1.Id)).Data.(*model.PostList).Posts[o1.Id]
|
||||
|
||||
if ro3.Message != msg {
|
||||
t.Fatal("Failed to update/get")
|
||||
}
|
||||
|
||||
ro4 := (<-store.Post().Get(o1.Id)).Data.(*model.PostList).Posts[o2.Id]
|
||||
|
||||
if ro4.Message != msg2 {
|
||||
t.Fatal("Failed to update/get")
|
||||
}
|
||||
|
||||
ro5 := (<-store.Post().Get(o3.Id)).Data.(*model.PostList).Posts[o3.Id]
|
||||
|
||||
if ro5.Message != msg3 && ro5.Hashtags != "#hashtag" {
|
||||
t.Fatal("Failed to update/get")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestPostStoreDelete(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
o1 := &model.Post{}
|
||||
o1.ChannelId = model.NewId()
|
||||
o1.UserId = model.NewId()
|
||||
o1.Message = "a" + model.NewId() + "b"
|
||||
|
||||
etag1 := (<-store.Post().GetEtag(o1.ChannelId)).Data.(string)
|
||||
if strings.Index(etag1, model.ETAG_ROOT_VERSION+".0.") != 0 {
|
||||
t.Fatal("Invalid Etag")
|
||||
}
|
||||
|
||||
o1 = (<-store.Post().Save(o1)).Data.(*model.Post)
|
||||
|
||||
if r1 := <-store.Post().Get(o1.Id); r1.Err != nil {
|
||||
t.Fatal(r1.Err)
|
||||
} else {
|
||||
if r1.Data.(*model.PostList).Posts[o1.Id].CreateAt != o1.CreateAt {
|
||||
t.Fatal("invalid returned post")
|
||||
}
|
||||
}
|
||||
|
||||
if r2 := <-store.Post().Delete(o1.Id, model.GetMillis()); r2.Err != nil {
|
||||
t.Fatal(r2.Err)
|
||||
}
|
||||
|
||||
if r3 := (<-store.Post().Get(o1.Id)); r3.Err == nil {
|
||||
t.Log(r3.Data)
|
||||
t.Fatal("Missing id should have failed")
|
||||
}
|
||||
|
||||
etag2 := (<-store.Post().GetEtag(o1.ChannelId)).Data.(string)
|
||||
if strings.Index(etag2, model.ETAG_ROOT_VERSION+"."+o1.Id) != 0 {
|
||||
t.Fatal("Invalid Etag")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPostStoreDelete1Level(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
o1 := &model.Post{}
|
||||
o1.ChannelId = model.NewId()
|
||||
o1.UserId = model.NewId()
|
||||
o1.Message = "a" + model.NewId() + "b"
|
||||
o1 = (<-store.Post().Save(o1)).Data.(*model.Post)
|
||||
|
||||
o2 := &model.Post{}
|
||||
o2.ChannelId = o1.ChannelId
|
||||
o2.UserId = model.NewId()
|
||||
o2.Message = "a" + model.NewId() + "b"
|
||||
o2.ParentId = o1.Id
|
||||
o2.RootId = o1.Id
|
||||
o2 = (<-store.Post().Save(o2)).Data.(*model.Post)
|
||||
|
||||
if r2 := <-store.Post().Delete(o1.Id, model.GetMillis()); r2.Err != nil {
|
||||
t.Fatal(r2.Err)
|
||||
}
|
||||
|
||||
if r3 := (<-store.Post().Get(o1.Id)); r3.Err == nil {
|
||||
t.Fatal("Deleted id should have failed")
|
||||
}
|
||||
|
||||
if r4 := (<-store.Post().Get(o2.Id)); r4.Err == nil {
|
||||
t.Fatal("Deleted id should have failed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPostStoreDelete2Level(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
o1 := &model.Post{}
|
||||
o1.ChannelId = model.NewId()
|
||||
o1.UserId = model.NewId()
|
||||
o1.Message = "a" + model.NewId() + "b"
|
||||
o1 = (<-store.Post().Save(o1)).Data.(*model.Post)
|
||||
|
||||
o2 := &model.Post{}
|
||||
o2.ChannelId = o1.ChannelId
|
||||
o2.UserId = model.NewId()
|
||||
o2.Message = "a" + model.NewId() + "b"
|
||||
o2.ParentId = o1.Id
|
||||
o2.RootId = o1.Id
|
||||
o2 = (<-store.Post().Save(o2)).Data.(*model.Post)
|
||||
|
||||
o3 := &model.Post{}
|
||||
o3.ChannelId = o1.ChannelId
|
||||
o3.UserId = model.NewId()
|
||||
o3.Message = "a" + model.NewId() + "b"
|
||||
o3.ParentId = o2.Id
|
||||
o3.RootId = o1.Id
|
||||
o3 = (<-store.Post().Save(o3)).Data.(*model.Post)
|
||||
|
||||
o4 := &model.Post{}
|
||||
o4.ChannelId = model.NewId()
|
||||
o4.UserId = model.NewId()
|
||||
o4.Message = "a" + model.NewId() + "b"
|
||||
o4 = (<-store.Post().Save(o4)).Data.(*model.Post)
|
||||
|
||||
if r2 := <-store.Post().Delete(o1.Id, model.GetMillis()); r2.Err != nil {
|
||||
t.Fatal(r2.Err)
|
||||
}
|
||||
|
||||
if r3 := (<-store.Post().Get(o1.Id)); r3.Err == nil {
|
||||
t.Fatal("Deleted id should have failed")
|
||||
}
|
||||
|
||||
if r4 := (<-store.Post().Get(o2.Id)); r4.Err == nil {
|
||||
t.Fatal("Deleted id should have failed")
|
||||
}
|
||||
|
||||
if r5 := (<-store.Post().Get(o3.Id)); r5.Err == nil {
|
||||
t.Fatal("Deleted id should have failed")
|
||||
}
|
||||
|
||||
if r6 := <-store.Post().Get(o4.Id); r6.Err != nil {
|
||||
t.Fatal(r6.Err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPostStoreGetWithChildren(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
o1 := &model.Post{}
|
||||
o1.ChannelId = model.NewId()
|
||||
o1.UserId = model.NewId()
|
||||
o1.Message = "a" + model.NewId() + "b"
|
||||
o1 = (<-store.Post().Save(o1)).Data.(*model.Post)
|
||||
|
||||
o2 := &model.Post{}
|
||||
o2.ChannelId = o1.ChannelId
|
||||
o2.UserId = model.NewId()
|
||||
o2.Message = "a" + model.NewId() + "b"
|
||||
o2.ParentId = o1.Id
|
||||
o2.RootId = o1.Id
|
||||
o2 = (<-store.Post().Save(o2)).Data.(*model.Post)
|
||||
|
||||
o3 := &model.Post{}
|
||||
o3.ChannelId = o1.ChannelId
|
||||
o3.UserId = model.NewId()
|
||||
o3.Message = "a" + model.NewId() + "b"
|
||||
o3.ParentId = o2.Id
|
||||
o3.RootId = o1.Id
|
||||
o3 = (<-store.Post().Save(o3)).Data.(*model.Post)
|
||||
|
||||
if r1 := <-store.Post().Get(o1.Id); r1.Err != nil {
|
||||
t.Fatal(r1.Err)
|
||||
} else {
|
||||
pl := r1.Data.(*model.PostList)
|
||||
if len(pl.Posts) != 3 {
|
||||
t.Fatal("invalid returned post")
|
||||
}
|
||||
}
|
||||
|
||||
<-store.Post().Delete(o3.Id, model.GetMillis())
|
||||
|
||||
if r2 := <-store.Post().Get(o1.Id); r2.Err != nil {
|
||||
t.Fatal(r2.Err)
|
||||
} else {
|
||||
pl := r2.Data.(*model.PostList)
|
||||
if len(pl.Posts) != 2 {
|
||||
t.Fatal("invalid returned post")
|
||||
}
|
||||
}
|
||||
|
||||
<-store.Post().Delete(o2.Id, model.GetMillis())
|
||||
|
||||
if r3 := <-store.Post().Get(o1.Id); r3.Err != nil {
|
||||
t.Fatal(r3.Err)
|
||||
} else {
|
||||
pl := r3.Data.(*model.PostList)
|
||||
if len(pl.Posts) != 1 {
|
||||
t.Fatal("invalid returned post")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPostStoreGetPostsWtihDetails(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
o1 := &model.Post{}
|
||||
o1.ChannelId = model.NewId()
|
||||
o1.UserId = model.NewId()
|
||||
o1.Message = "a" + model.NewId() + "b"
|
||||
o1 = (<-store.Post().Save(o1)).Data.(*model.Post)
|
||||
time.Sleep(2 * time.Millisecond)
|
||||
|
||||
o2 := &model.Post{}
|
||||
o2.ChannelId = o1.ChannelId
|
||||
o2.UserId = model.NewId()
|
||||
o2.Message = "a" + model.NewId() + "b"
|
||||
o2.ParentId = o1.Id
|
||||
o2.RootId = o1.Id
|
||||
o2 = (<-store.Post().Save(o2)).Data.(*model.Post)
|
||||
time.Sleep(2 * time.Millisecond)
|
||||
|
||||
o2a := &model.Post{}
|
||||
o2a.ChannelId = o1.ChannelId
|
||||
o2a.UserId = model.NewId()
|
||||
o2a.Message = "a" + model.NewId() + "b"
|
||||
o2a.ParentId = o1.Id
|
||||
o2a.RootId = o1.Id
|
||||
o2a = (<-store.Post().Save(o2a)).Data.(*model.Post)
|
||||
time.Sleep(2 * time.Millisecond)
|
||||
|
||||
o3 := &model.Post{}
|
||||
o3.ChannelId = o1.ChannelId
|
||||
o3.UserId = model.NewId()
|
||||
o3.Message = "a" + model.NewId() + "b"
|
||||
o3.ParentId = o1.Id
|
||||
o3.RootId = o1.Id
|
||||
o3 = (<-store.Post().Save(o3)).Data.(*model.Post)
|
||||
time.Sleep(2 * time.Millisecond)
|
||||
|
||||
o4 := &model.Post{}
|
||||
o4.ChannelId = o1.ChannelId
|
||||
o4.UserId = model.NewId()
|
||||
o4.Message = "a" + model.NewId() + "b"
|
||||
o4 = (<-store.Post().Save(o4)).Data.(*model.Post)
|
||||
time.Sleep(2 * time.Millisecond)
|
||||
|
||||
o5 := &model.Post{}
|
||||
o5.ChannelId = o1.ChannelId
|
||||
o5.UserId = model.NewId()
|
||||
o5.Message = "a" + model.NewId() + "b"
|
||||
o5.ParentId = o4.Id
|
||||
o5.RootId = o4.Id
|
||||
o5 = (<-store.Post().Save(o5)).Data.(*model.Post)
|
||||
|
||||
r1 := (<-store.Post().GetPosts(o1.ChannelId, 0, 4)).Data.(*model.PostList)
|
||||
|
||||
if r1.Order[0] != o5.Id {
|
||||
t.Fatal("invalid order")
|
||||
}
|
||||
|
||||
if r1.Order[1] != o4.Id {
|
||||
t.Fatal("invalid order")
|
||||
}
|
||||
|
||||
if r1.Order[2] != o3.Id {
|
||||
t.Fatal("invalid order")
|
||||
}
|
||||
|
||||
if r1.Order[3] != o2a.Id {
|
||||
t.Fatal("invalid order")
|
||||
}
|
||||
|
||||
if len(r1.Posts) != 6 {
|
||||
t.Fatal("wrong size")
|
||||
}
|
||||
|
||||
if r1.Posts[o1.Id].Message != o1.Message {
|
||||
t.Fatal("Missing parent")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPostStoreSearch(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
teamId := model.NewId()
|
||||
userId := model.NewId()
|
||||
|
||||
c1 := &model.Channel{}
|
||||
c1.TeamId = teamId
|
||||
c1.DisplayName = "Channel1"
|
||||
c1.Name = "a" + model.NewId() + "b"
|
||||
c1.Type = model.CHANNEL_OPEN
|
||||
c1 = (<-store.Channel().Save(c1)).Data.(*model.Channel)
|
||||
|
||||
m1 := model.ChannelMember{}
|
||||
m1.ChannelId = c1.Id
|
||||
m1.UserId = userId
|
||||
m1.NotifyLevel = model.CHANNEL_NOTIFY_ALL
|
||||
<-store.Channel().SaveMember(&m1)
|
||||
|
||||
c2 := &model.Channel{}
|
||||
c2.TeamId = teamId
|
||||
c2.DisplayName = "Channel1"
|
||||
c2.Name = "a" + model.NewId() + "b"
|
||||
c2.Type = model.CHANNEL_OPEN
|
||||
c2 = (<-store.Channel().Save(c2)).Data.(*model.Channel)
|
||||
|
||||
o1 := &model.Post{}
|
||||
o1.ChannelId = c1.Id
|
||||
o1.UserId = model.NewId()
|
||||
o1.Message = "corey mattermost new york"
|
||||
o1 = (<-store.Post().Save(o1)).Data.(*model.Post)
|
||||
|
||||
o2 := &model.Post{}
|
||||
o2.ChannelId = c1.Id
|
||||
o2.UserId = model.NewId()
|
||||
o2.Message = "New Jersey is where John is from"
|
||||
o2 = (<-store.Post().Save(o2)).Data.(*model.Post)
|
||||
|
||||
o3 := &model.Post{}
|
||||
o3.ChannelId = c2.Id
|
||||
o3.UserId = model.NewId()
|
||||
o3.Message = "New Jersey is where John is from corey new york"
|
||||
o3 = (<-store.Post().Save(o3)).Data.(*model.Post)
|
||||
|
||||
o4 := &model.Post{}
|
||||
o4.ChannelId = c1.Id
|
||||
o4.UserId = model.NewId()
|
||||
o4.Hashtags = "#hashtag"
|
||||
o4.Message = "message"
|
||||
o4 = (<-store.Post().Save(o4)).Data.(*model.Post)
|
||||
|
||||
o5 := &model.Post{}
|
||||
o5.ChannelId = c1.Id
|
||||
o5.UserId = model.NewId()
|
||||
o5.Hashtags = "#secret #howdy"
|
||||
o5 = (<-store.Post().Save(o5)).Data.(*model.Post)
|
||||
|
||||
r1 := (<-store.Post().Search(teamId, userId, "corey", false)).Data.(*model.PostList)
|
||||
if len(r1.Order) != 1 && r1.Order[0] != o1.Id {
|
||||
t.Fatal("returned wrong serach result")
|
||||
}
|
||||
|
||||
r2 := (<-store.Post().Search(teamId, userId, "new york", false)).Data.(*model.PostList)
|
||||
if len(r2.Order) != 2 && r2.Order[0] != o2.Id {
|
||||
t.Fatal("returned wrong serach result")
|
||||
}
|
||||
|
||||
r3 := (<-store.Post().Search(teamId, userId, "new", false)).Data.(*model.PostList)
|
||||
if len(r3.Order) != 2 && r3.Order[0] != o1.Id {
|
||||
t.Fatal("returned wrong serach result")
|
||||
}
|
||||
|
||||
r4 := (<-store.Post().Search(teamId, userId, "john", false)).Data.(*model.PostList)
|
||||
if len(r4.Order) != 1 && r4.Order[0] != o2.Id {
|
||||
t.Fatal("returned wrong serach result")
|
||||
}
|
||||
|
||||
r5 := (<-store.Post().Search(teamId, userId, "matter*", false)).Data.(*model.PostList)
|
||||
if len(r5.Order) != 1 && r5.Order[0] != o1.Id {
|
||||
t.Fatal("returned wrong serach result")
|
||||
}
|
||||
|
||||
r6 := (<-store.Post().Search(teamId, userId, "#hashtag", true)).Data.(*model.PostList)
|
||||
if len(r6.Order) != 1 && r6.Order[0] != o4.Id {
|
||||
t.Fatal("returned wrong serach result")
|
||||
}
|
||||
|
||||
r7 := (<-store.Post().Search(teamId, userId, "#secret", true)).Data.(*model.PostList)
|
||||
if len(r7.Order) != 1 && r7.Order[0] != o5.Id {
|
||||
t.Fatal("returned wrong serach result")
|
||||
}
|
||||
|
||||
r8 := (<-store.Post().Search(teamId, userId, "@thisshouldmatchnothing", true)).Data.(*model.PostList)
|
||||
if len(r8.Order) != 0 {
|
||||
t.Fatal("returned wrong serach result")
|
||||
}
|
||||
}
|
||||
176
store/sql_session_store.go
Обычный файл
176
store/sql_session_store.go
Обычный файл
@@ -0,0 +1,176 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package store
|
||||
|
||||
import (
|
||||
l4g "code.google.com/p/log4go"
|
||||
"github.com/mattermost/platform/model"
|
||||
)
|
||||
|
||||
type SqlSessionStore struct {
|
||||
*SqlStore
|
||||
}
|
||||
|
||||
func NewSqlSessionStore(sqlStore *SqlStore) SessionStore {
|
||||
us := &SqlSessionStore{sqlStore}
|
||||
|
||||
for _, db := range sqlStore.GetAllConns() {
|
||||
table := db.AddTableWithName(model.Session{}, "Sessions").SetKeys(false, "Id")
|
||||
table.ColMap("Id").SetMaxSize(26)
|
||||
table.ColMap("AltId").SetMaxSize(26)
|
||||
table.ColMap("UserId").SetMaxSize(26)
|
||||
table.ColMap("TeamId").SetMaxSize(26)
|
||||
table.ColMap("DeviceId").SetMaxSize(128)
|
||||
table.ColMap("Roles").SetMaxSize(64)
|
||||
table.ColMap("Props").SetMaxSize(1000)
|
||||
}
|
||||
|
||||
return us
|
||||
}
|
||||
|
||||
func (me SqlSessionStore) UpgradeSchemaIfNeeded() {
|
||||
}
|
||||
|
||||
func (me SqlSessionStore) CreateIndexesIfNotExists() {
|
||||
me.CreateIndexIfNotExists("idx_user_id", "Sessions", "UserId")
|
||||
}
|
||||
|
||||
func (me SqlSessionStore) Save(session *model.Session) StoreChannel {
|
||||
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
result := StoreResult{}
|
||||
|
||||
if len(session.Id) > 0 {
|
||||
result.Err = model.NewAppError("SqlSessionStore.Save", "Cannot update existing session", "id="+session.Id)
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
return
|
||||
}
|
||||
|
||||
session.PreSave()
|
||||
|
||||
if cur := <-me.CleanUpExpiredSessions(session.UserId); cur.Err != nil {
|
||||
l4g.Error("Failed to cleanup sessions in Save err=%v", cur.Err)
|
||||
}
|
||||
|
||||
if err := me.GetMaster().Insert(session); err != nil {
|
||||
result.Err = model.NewAppError("SqlSessionStore.Save", "We couldn't save the session", "id="+session.Id+", "+err.Error())
|
||||
} else {
|
||||
result.Data = session
|
||||
}
|
||||
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
}()
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
|
||||
func (me SqlSessionStore) Get(id string) StoreChannel {
|
||||
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
result := StoreResult{}
|
||||
|
||||
if obj, err := me.GetReplica().Get(model.Session{}, id); err != nil {
|
||||
result.Err = model.NewAppError("SqlSessionStore.Get", "We encounted an error finding the session", "id="+id+", "+err.Error())
|
||||
} else if obj == nil {
|
||||
result.Err = model.NewAppError("SqlSessionStore.Get", "We couldn't find the existing session", "id="+id)
|
||||
} else {
|
||||
result.Data = obj.(*model.Session)
|
||||
}
|
||||
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
|
||||
}()
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
|
||||
func (me SqlSessionStore) GetSessions(userId string) StoreChannel {
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
|
||||
if cur := <-me.CleanUpExpiredSessions(userId); cur.Err != nil {
|
||||
l4g.Error("Failed to cleanup sessions in getSessions err=%v", cur.Err)
|
||||
}
|
||||
|
||||
result := StoreResult{}
|
||||
|
||||
var sessions []*model.Session
|
||||
|
||||
if _, err := me.GetReplica().Select(&sessions, "SELECT * FROM Sessions WHERE UserId = ? ORDER BY LastActivityAt DESC", userId); err != nil {
|
||||
result.Err = model.NewAppError("SqlSessionStore.GetSessions", "We encounted an error while finding user sessions", err.Error())
|
||||
} else {
|
||||
|
||||
result.Data = sessions
|
||||
}
|
||||
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
}()
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
|
||||
func (me SqlSessionStore) Remove(sessionIdOrAlt string) StoreChannel {
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
result := StoreResult{}
|
||||
|
||||
_, err := me.GetMaster().Exec("DELETE FROM Sessions WHERE Id = ? Or AltId = ?", sessionIdOrAlt, sessionIdOrAlt)
|
||||
if err != nil {
|
||||
result.Err = model.NewAppError("SqlSessionStore.RemoveSession", "We couldn't remove the session", "id="+sessionIdOrAlt+", err="+err.Error())
|
||||
}
|
||||
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
}()
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
|
||||
func (me SqlSessionStore) CleanUpExpiredSessions(userId string) StoreChannel {
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
result := StoreResult{}
|
||||
|
||||
if _, err := me.GetMaster().Exec("DELETE FROM Sessions WHERE UserId = ? AND ExpiresAt != 0 AND ? > ExpiresAt", userId, model.GetMillis()); err != nil {
|
||||
result.Err = model.NewAppError("SqlSessionStore.CleanUpExpiredSessions", "We encounted an error while deleting expired user sessions", err.Error())
|
||||
} else {
|
||||
result.Data = userId
|
||||
}
|
||||
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
}()
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
|
||||
func (me SqlSessionStore) UpdateLastActivityAt(sessionId string, time int64) StoreChannel {
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
result := StoreResult{}
|
||||
|
||||
if _, err := me.GetMaster().Exec("UPDATE Sessions SET LastActivityAt = ? WHERE Id = ?", time, sessionId); err != nil {
|
||||
result.Err = model.NewAppError("SqlSessionStore.UpdateLastActivityAt", "We couldn't update the last_activity_at", "sessionId="+sessionId)
|
||||
} else {
|
||||
result.Data = sessionId
|
||||
}
|
||||
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
}()
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
134
store/sql_session_store_test.go
Обычный файл
134
store/sql_session_store_test.go
Обычный файл
@@ -0,0 +1,134 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package store
|
||||
|
||||
import (
|
||||
"github.com/mattermost/platform/model"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSessionStoreSave(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
s1 := model.Session{}
|
||||
s1.UserId = model.NewId()
|
||||
s1.TeamId = model.NewId()
|
||||
|
||||
if err := (<-store.Session().Save(&s1)).Err; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSessionGet(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
s1 := model.Session{}
|
||||
s1.UserId = model.NewId()
|
||||
s1.TeamId = model.NewId()
|
||||
<-store.Session().Save(&s1)
|
||||
|
||||
s2 := model.Session{}
|
||||
s2.UserId = s1.UserId
|
||||
s2.TeamId = s1.TeamId
|
||||
<-store.Session().Save(&s2)
|
||||
|
||||
s3 := model.Session{}
|
||||
s3.UserId = s1.UserId
|
||||
s3.TeamId = s1.TeamId
|
||||
s3.ExpiresAt = 1
|
||||
<-store.Session().Save(&s3)
|
||||
|
||||
if rs1 := (<-store.Session().Get(s1.Id)); rs1.Err != nil {
|
||||
t.Fatal(rs1.Err)
|
||||
} else {
|
||||
if rs1.Data.(*model.Session).Id != s1.Id {
|
||||
t.Fatal("should match")
|
||||
}
|
||||
}
|
||||
|
||||
if rs2 := (<-store.Session().GetSessions(s1.UserId)); rs2.Err != nil {
|
||||
t.Fatal(rs2.Err)
|
||||
} else {
|
||||
if len(rs2.Data.([]*model.Session)) != 2 {
|
||||
t.Fatal("should match len")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestSessionRemove(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
s1 := model.Session{}
|
||||
s1.UserId = model.NewId()
|
||||
s1.TeamId = model.NewId()
|
||||
<-store.Session().Save(&s1)
|
||||
|
||||
if rs1 := (<-store.Session().Get(s1.Id)); rs1.Err != nil {
|
||||
t.Fatal(rs1.Err)
|
||||
} else {
|
||||
if rs1.Data.(*model.Session).Id != s1.Id {
|
||||
t.Fatal("should match")
|
||||
}
|
||||
}
|
||||
|
||||
<-store.Session().Remove(s1.Id)
|
||||
|
||||
if rs2 := (<-store.Session().Get(s1.Id)); rs2.Err == nil {
|
||||
t.Fatal("should have been removed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSessionRemoveAlt(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
s1 := model.Session{}
|
||||
s1.UserId = model.NewId()
|
||||
s1.TeamId = model.NewId()
|
||||
<-store.Session().Save(&s1)
|
||||
|
||||
if rs1 := (<-store.Session().Get(s1.Id)); rs1.Err != nil {
|
||||
t.Fatal(rs1.Err)
|
||||
} else {
|
||||
if rs1.Data.(*model.Session).Id != s1.Id {
|
||||
t.Fatal("should match")
|
||||
}
|
||||
}
|
||||
|
||||
<-store.Session().Remove(s1.AltId)
|
||||
|
||||
if rs2 := (<-store.Session().Get(s1.Id)); rs2.Err == nil {
|
||||
t.Fatal("should have been removed")
|
||||
}
|
||||
|
||||
if rs3 := (<-store.Session().GetSessions(s1.UserId)); rs3.Err != nil {
|
||||
t.Fatal(rs3.Err)
|
||||
} else {
|
||||
if len(rs3.Data.([]*model.Session)) != 0 {
|
||||
t.Fatal("should match len")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSessionStoreUpdateLastActivityAt(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
s1 := model.Session{}
|
||||
s1.UserId = model.NewId()
|
||||
s1.TeamId = model.NewId()
|
||||
<-store.Session().Save(&s1)
|
||||
|
||||
if err := (<-store.Session().UpdateLastActivityAt(s1.Id, 1234567890)).Err; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if r1 := <-store.Session().Get(s1.Id); r1.Err != nil {
|
||||
t.Fatal(r1.Err)
|
||||
} else {
|
||||
if r1.Data.(*model.Session).LastActivityAt != 1234567890 {
|
||||
t.Fatal("LastActivityAt not updated correctly")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
372
store/sql_store.go
Обычный файл
372
store/sql_store.go
Обычный файл
@@ -0,0 +1,372 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package store
|
||||
|
||||
import (
|
||||
l4g "code.google.com/p/log4go"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
crand "crypto/rand"
|
||||
dbsql "database/sql"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/go-gorp/gorp"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/mattermost/platform/model"
|
||||
"github.com/mattermost/platform/utils"
|
||||
"io"
|
||||
sqltrace "log"
|
||||
"math/rand"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
type SqlStore struct {
|
||||
master *gorp.DbMap
|
||||
replicas []*gorp.DbMap
|
||||
team TeamStore
|
||||
channel ChannelStore
|
||||
post PostStore
|
||||
user UserStore
|
||||
audit AuditStore
|
||||
session SessionStore
|
||||
}
|
||||
|
||||
func NewSqlStore() Store {
|
||||
|
||||
sqlStore := &SqlStore{}
|
||||
|
||||
sqlStore.master = setupConnection("master", utils.Cfg.SqlSettings.DriverName,
|
||||
utils.Cfg.SqlSettings.DataSource, utils.Cfg.SqlSettings.MaxIdleConns,
|
||||
utils.Cfg.SqlSettings.MaxOpenConns, utils.Cfg.SqlSettings.Trace)
|
||||
|
||||
sqlStore.replicas = make([]*gorp.DbMap, len(utils.Cfg.SqlSettings.DataSourceReplicas))
|
||||
for i, replica := range utils.Cfg.SqlSettings.DataSourceReplicas {
|
||||
sqlStore.replicas[i] = setupConnection(fmt.Sprintf("replica-%v", i), utils.Cfg.SqlSettings.DriverName, replica,
|
||||
utils.Cfg.SqlSettings.MaxIdleConns, utils.Cfg.SqlSettings.MaxOpenConns,
|
||||
utils.Cfg.SqlSettings.Trace)
|
||||
}
|
||||
|
||||
sqlStore.team = NewSqlTeamStore(sqlStore)
|
||||
sqlStore.channel = NewSqlChannelStore(sqlStore)
|
||||
sqlStore.post = NewSqlPostStore(sqlStore)
|
||||
sqlStore.user = NewSqlUserStore(sqlStore)
|
||||
sqlStore.audit = NewSqlAuditStore(sqlStore)
|
||||
sqlStore.session = NewSqlSessionStore(sqlStore)
|
||||
|
||||
sqlStore.master.CreateTablesIfNotExists()
|
||||
|
||||
sqlStore.team.(*SqlTeamStore).CreateIndexesIfNotExists()
|
||||
sqlStore.channel.(*SqlChannelStore).CreateIndexesIfNotExists()
|
||||
sqlStore.post.(*SqlPostStore).CreateIndexesIfNotExists()
|
||||
sqlStore.user.(*SqlUserStore).CreateIndexesIfNotExists()
|
||||
sqlStore.audit.(*SqlAuditStore).CreateIndexesIfNotExists()
|
||||
sqlStore.session.(*SqlSessionStore).CreateIndexesIfNotExists()
|
||||
|
||||
sqlStore.team.(*SqlTeamStore).UpgradeSchemaIfNeeded()
|
||||
sqlStore.channel.(*SqlChannelStore).UpgradeSchemaIfNeeded()
|
||||
sqlStore.post.(*SqlPostStore).UpgradeSchemaIfNeeded()
|
||||
sqlStore.user.(*SqlUserStore).UpgradeSchemaIfNeeded()
|
||||
sqlStore.audit.(*SqlAuditStore).UpgradeSchemaIfNeeded()
|
||||
sqlStore.session.(*SqlSessionStore).UpgradeSchemaIfNeeded()
|
||||
|
||||
return sqlStore
|
||||
}
|
||||
|
||||
func setupConnection(con_type string, driver string, dataSource string, maxIdle int, maxOpen int, trace bool) *gorp.DbMap {
|
||||
|
||||
db, err := dbsql.Open(driver, dataSource)
|
||||
if err != nil {
|
||||
l4g.Critical("Failed to open sql connection to '%v' err:%v", dataSource, err)
|
||||
time.Sleep(time.Second)
|
||||
panic("Failed to open sql connection" + err.Error())
|
||||
}
|
||||
|
||||
l4g.Info("Pinging sql %v database at '%v'", con_type, dataSource)
|
||||
err = db.Ping()
|
||||
if err != nil {
|
||||
l4g.Critical("Failed to ping db err:%v", err)
|
||||
time.Sleep(time.Second)
|
||||
panic("Failed to open sql connection " + err.Error())
|
||||
}
|
||||
|
||||
db.SetMaxIdleConns(maxIdle)
|
||||
db.SetMaxOpenConns(maxOpen)
|
||||
|
||||
var dbmap *gorp.DbMap
|
||||
|
||||
if driver == "sqlite3" {
|
||||
dbmap = &gorp.DbMap{Db: db, TypeConverter: mattermConverter{}, Dialect: gorp.SqliteDialect{}}
|
||||
} else if driver == "mysql" {
|
||||
dbmap = &gorp.DbMap{Db: db, TypeConverter: mattermConverter{}, Dialect: gorp.MySQLDialect{Engine: "InnoDB", Encoding: "UTF8"}}
|
||||
} else {
|
||||
l4g.Critical("Failed to create dialect specific driver")
|
||||
time.Sleep(time.Second)
|
||||
panic("Failed to create dialect specific driver " + err.Error())
|
||||
}
|
||||
|
||||
if trace {
|
||||
dbmap.TraceOn("", sqltrace.New(os.Stdout, "sql-trace:", sqltrace.Lmicroseconds))
|
||||
}
|
||||
|
||||
return dbmap
|
||||
}
|
||||
|
||||
func (ss SqlStore) CreateColumnIfNotExists(tableName string, columnName string, afterName string, colType string, defaultValue string) bool {
|
||||
count, err := ss.GetMaster().SelectInt(
|
||||
`SELECT
|
||||
COUNT(0) AS column_exists
|
||||
FROM
|
||||
information_schema.COLUMNS
|
||||
WHERE
|
||||
TABLE_SCHEMA = DATABASE()
|
||||
AND TABLE_NAME = ?
|
||||
AND COLUMN_NAME = ?`,
|
||||
tableName,
|
||||
columnName,
|
||||
)
|
||||
if err != nil {
|
||||
l4g.Critical("Failed to check if column exists %v", err)
|
||||
time.Sleep(time.Second)
|
||||
panic("Failed to check if column exists " + err.Error())
|
||||
}
|
||||
|
||||
if count > 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
_, err = ss.GetMaster().Exec("ALTER TABLE " + tableName + " ADD " + columnName + " " + colType + " DEFAULT '" + defaultValue + "'" + " AFTER " + afterName)
|
||||
if err != nil {
|
||||
l4g.Critical("Failed to create column %v", err)
|
||||
time.Sleep(time.Second)
|
||||
panic("Failed to create column " + err.Error())
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (ss SqlStore) RemoveColumnIfExists(tableName string, columnName string) bool {
|
||||
count, err := ss.GetMaster().SelectInt(
|
||||
`SELECT
|
||||
COUNT(0) AS column_exists
|
||||
FROM
|
||||
information_schema.COLUMNS
|
||||
WHERE
|
||||
TABLE_SCHEMA = DATABASE()
|
||||
AND TABLE_NAME = ?
|
||||
AND COLUMN_NAME = ?`,
|
||||
tableName,
|
||||
columnName,
|
||||
)
|
||||
if err != nil {
|
||||
l4g.Critical("Failed to check if column exists %v", err)
|
||||
time.Sleep(time.Second)
|
||||
panic("Failed to check if column exists " + err.Error())
|
||||
}
|
||||
|
||||
if count == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
_, err = ss.GetMaster().Exec("ALTER TABLE " + tableName + " DROP COLUMN " + columnName)
|
||||
if err != nil {
|
||||
l4g.Critical("Failed to drop column %v", err)
|
||||
time.Sleep(time.Second)
|
||||
panic("Failed to drop column " + err.Error())
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (ss SqlStore) CreateIndexIfNotExists(indexName string, tableName string, columnName string) {
|
||||
ss.createIndexIfNotExists(indexName, tableName, columnName, false)
|
||||
}
|
||||
|
||||
func (ss SqlStore) CreateFullTextIndexIfNotExists(indexName string, tableName string, columnName string) {
|
||||
ss.createIndexIfNotExists(indexName, tableName, columnName, true)
|
||||
}
|
||||
|
||||
func (ss SqlStore) createIndexIfNotExists(indexName string, tableName string, columnName string, fullText bool) {
|
||||
count, err := ss.GetMaster().SelectInt("SELECT COUNT(0) AS index_exists FROM information_schema.statistics WHERE TABLE_SCHEMA = DATABASE() and table_name = ? AND index_name = ?", tableName, indexName)
|
||||
if err != nil {
|
||||
l4g.Critical("Failed to check index", err)
|
||||
time.Sleep(time.Second)
|
||||
panic("Failed to check index" + err.Error())
|
||||
}
|
||||
|
||||
if count > 0 {
|
||||
return
|
||||
}
|
||||
|
||||
fullTextIndex := ""
|
||||
if fullText {
|
||||
fullTextIndex = " FULLTEXT "
|
||||
}
|
||||
|
||||
_, err = ss.GetMaster().Exec("CREATE " + fullTextIndex + " INDEX " + indexName + " ON " + tableName + " (" + columnName + ")")
|
||||
if err != nil {
|
||||
l4g.Critical("Failed to create index", err)
|
||||
time.Sleep(time.Second)
|
||||
panic("Failed to create index " + err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func (ss SqlStore) GetMaster() *gorp.DbMap {
|
||||
return ss.master
|
||||
}
|
||||
|
||||
func (ss SqlStore) GetReplica() *gorp.DbMap {
|
||||
return ss.replicas[rand.Intn(len(ss.replicas))]
|
||||
}
|
||||
|
||||
func (ss SqlStore) GetAllConns() []*gorp.DbMap {
|
||||
all := make([]*gorp.DbMap, len(ss.replicas)+1)
|
||||
copy(all, ss.replicas)
|
||||
all[len(ss.replicas)] = ss.master
|
||||
return all
|
||||
}
|
||||
|
||||
func (ss SqlStore) Close() {
|
||||
l4g.Info("Closing SqlStore")
|
||||
ss.master.Db.Close()
|
||||
for _, replica := range ss.replicas {
|
||||
replica.Db.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func (ss SqlStore) Team() TeamStore {
|
||||
return ss.team
|
||||
}
|
||||
|
||||
func (ss SqlStore) Channel() ChannelStore {
|
||||
return ss.channel
|
||||
}
|
||||
|
||||
func (ss SqlStore) Post() PostStore {
|
||||
return ss.post
|
||||
}
|
||||
|
||||
func (ss SqlStore) User() UserStore {
|
||||
return ss.user
|
||||
}
|
||||
|
||||
func (ss SqlStore) Session() SessionStore {
|
||||
return ss.session
|
||||
}
|
||||
|
||||
func (ss SqlStore) Audit() AuditStore {
|
||||
return ss.audit
|
||||
}
|
||||
|
||||
type mattermConverter struct{}
|
||||
|
||||
func (me mattermConverter) ToDb(val interface{}) (interface{}, error) {
|
||||
|
||||
switch t := val.(type) {
|
||||
case model.StringMap:
|
||||
return model.MapToJson(t), nil
|
||||
case model.StringArray:
|
||||
return model.ArrayToJson(t), nil
|
||||
case model.EncryptStringMap:
|
||||
return encrypt([]byte(utils.Cfg.SqlSettings.AtRestEncryptKey), model.MapToJson(t))
|
||||
}
|
||||
|
||||
return val, nil
|
||||
}
|
||||
|
||||
func (me mattermConverter) FromDb(target interface{}) (gorp.CustomScanner, bool) {
|
||||
switch target.(type) {
|
||||
case *model.StringMap:
|
||||
binder := func(holder, target interface{}) error {
|
||||
s, ok := holder.(*string)
|
||||
if !ok {
|
||||
return errors.New("FromDb: Unable to convert StringMap to *string")
|
||||
}
|
||||
b := []byte(*s)
|
||||
return json.Unmarshal(b, target)
|
||||
}
|
||||
return gorp.CustomScanner{new(string), target, binder}, true
|
||||
case *model.StringArray:
|
||||
binder := func(holder, target interface{}) error {
|
||||
s, ok := holder.(*string)
|
||||
if !ok {
|
||||
return errors.New("FromDb: Unable to convert StringArray to *string")
|
||||
}
|
||||
b := []byte(*s)
|
||||
return json.Unmarshal(b, target)
|
||||
}
|
||||
return gorp.CustomScanner{new(string), target, binder}, true
|
||||
case *model.EncryptStringMap:
|
||||
binder := func(holder, target interface{}) error {
|
||||
s, ok := holder.(*string)
|
||||
if !ok {
|
||||
return errors.New("FromDb: Unable to convert EncryptStringMap to *string")
|
||||
}
|
||||
|
||||
ue, err := decrypt([]byte(utils.Cfg.SqlSettings.AtRestEncryptKey), *s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
b := []byte(ue)
|
||||
return json.Unmarshal(b, target)
|
||||
}
|
||||
return gorp.CustomScanner{new(string), target, binder}, true
|
||||
}
|
||||
|
||||
return gorp.CustomScanner{}, false
|
||||
}
|
||||
|
||||
func encrypt(key []byte, text string) (string, error) {
|
||||
|
||||
if text == "" || text == "{}" {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
plaintext := []byte(text)
|
||||
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
ciphertext := make([]byte, aes.BlockSize+len(plaintext))
|
||||
iv := ciphertext[:aes.BlockSize]
|
||||
if _, err := io.ReadFull(crand.Reader, iv); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
stream := cipher.NewCFBEncrypter(block, iv)
|
||||
stream.XORKeyStream(ciphertext[aes.BlockSize:], plaintext)
|
||||
|
||||
return base64.URLEncoding.EncodeToString(ciphertext), nil
|
||||
}
|
||||
|
||||
func decrypt(key []byte, cryptoText string) (string, error) {
|
||||
|
||||
if cryptoText == "" || cryptoText == "{}" {
|
||||
return "{}", nil
|
||||
}
|
||||
|
||||
ciphertext, _ := base64.URLEncoding.DecodeString(cryptoText)
|
||||
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if len(ciphertext) < aes.BlockSize {
|
||||
return "", errors.New("ciphertext too short")
|
||||
}
|
||||
iv := ciphertext[:aes.BlockSize]
|
||||
ciphertext = ciphertext[aes.BlockSize:]
|
||||
|
||||
stream := cipher.NewCFBDecrypter(block, iv)
|
||||
|
||||
stream.XORKeyStream(ciphertext, ciphertext)
|
||||
|
||||
return fmt.Sprintf("%s", ciphertext), nil
|
||||
}
|
||||
83
store/sql_store_test.go
Обычный файл
83
store/sql_store_test.go
Обычный файл
@@ -0,0 +1,83 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package store
|
||||
|
||||
import (
|
||||
"github.com/mattermost/platform/model"
|
||||
"github.com/mattermost/platform/utils"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var store Store
|
||||
|
||||
func Setup() {
|
||||
if store == nil {
|
||||
utils.LoadConfig("config.json")
|
||||
store = NewSqlStore()
|
||||
}
|
||||
}
|
||||
|
||||
func TestSqlStore1(t *testing.T) {
|
||||
utils.LoadConfig("config.json")
|
||||
utils.Cfg.SqlSettings.Trace = true
|
||||
|
||||
store := NewSqlStore()
|
||||
store.Close()
|
||||
|
||||
utils.LoadConfig("config.json")
|
||||
}
|
||||
|
||||
func TestSqlStore2(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r == nil {
|
||||
t.Fatal("should have been fatal")
|
||||
}
|
||||
}()
|
||||
|
||||
utils.LoadConfig("config.json")
|
||||
utils.Cfg.SqlSettings.DriverName = "missing"
|
||||
store = NewSqlStore()
|
||||
|
||||
utils.LoadConfig("config.json")
|
||||
}
|
||||
|
||||
func TestSqlStore3(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r == nil {
|
||||
t.Fatal("should have been fatal")
|
||||
}
|
||||
}()
|
||||
|
||||
utils.LoadConfig("config.json")
|
||||
utils.Cfg.SqlSettings.DataSource = "missing"
|
||||
store = NewSqlStore()
|
||||
|
||||
utils.LoadConfig("config.json")
|
||||
}
|
||||
|
||||
func TestEncrypt(t *testing.T) {
|
||||
m := make(map[string]string)
|
||||
|
||||
key := []byte("IPc17oYK9NAj6WfJeCqm5AxIBF6WBNuN") // AES-256
|
||||
|
||||
originalText1 := model.MapToJson(m)
|
||||
cryptoText1, _ := encrypt(key, originalText1)
|
||||
text1, _ := decrypt(key, cryptoText1)
|
||||
rm1 := model.MapFromJson(strings.NewReader(text1))
|
||||
|
||||
if len(rm1) != 0 {
|
||||
t.Fatal("error in encrypt")
|
||||
}
|
||||
|
||||
m["key"] = "value"
|
||||
originalText2 := model.MapToJson(m)
|
||||
cryptoText2, _ := encrypt(key, originalText2)
|
||||
text2, _ := decrypt(key, cryptoText2)
|
||||
rm2 := model.MapFromJson(strings.NewReader(text2))
|
||||
|
||||
if rm2["key"] != "value" {
|
||||
t.Fatal("error in encrypt")
|
||||
}
|
||||
}
|
||||
195
store/sql_team_store.go
Обычный файл
195
store/sql_team_store.go
Обычный файл
@@ -0,0 +1,195 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package store
|
||||
|
||||
import (
|
||||
"github.com/mattermost/platform/model"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type SqlTeamStore struct {
|
||||
*SqlStore
|
||||
}
|
||||
|
||||
func NewSqlTeamStore(sqlStore *SqlStore) TeamStore {
|
||||
s := &SqlTeamStore{sqlStore}
|
||||
|
||||
for _, db := range sqlStore.GetAllConns() {
|
||||
table := db.AddTableWithName(model.Team{}, "Teams").SetKeys(false, "Id")
|
||||
table.ColMap("Id").SetMaxSize(26)
|
||||
table.ColMap("Name").SetMaxSize(64)
|
||||
table.ColMap("Domain").SetMaxSize(64).SetUnique(true)
|
||||
table.ColMap("Email").SetMaxSize(128)
|
||||
table.ColMap("CompanyName").SetMaxSize(64)
|
||||
table.ColMap("AllowedDomains").SetMaxSize(500)
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func (s SqlTeamStore) UpgradeSchemaIfNeeded() {
|
||||
}
|
||||
|
||||
func (s SqlTeamStore) CreateIndexesIfNotExists() {
|
||||
}
|
||||
|
||||
func (s SqlTeamStore) Save(team *model.Team) StoreChannel {
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
result := StoreResult{}
|
||||
|
||||
if len(team.Id) > 0 {
|
||||
result.Err = model.NewAppError("SqlTeamStore.Save",
|
||||
"Must call update for exisiting team", "id="+team.Id)
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
return
|
||||
}
|
||||
|
||||
team.PreSave()
|
||||
if result.Err = team.IsValid(); result.Err != nil {
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
return
|
||||
}
|
||||
|
||||
if err := s.GetMaster().Insert(team); err != nil {
|
||||
if strings.Contains(err.Error(), "Duplicate entry") && strings.Contains(err.Error(), "for key 'Domain'") {
|
||||
result.Err = model.NewAppError("SqlTeamStore.Save", "A team with that domain already exists", "id="+team.Id+", "+err.Error())
|
||||
} else {
|
||||
result.Err = model.NewAppError("SqlTeamStore.Save", "We couldn't save the team", "id="+team.Id+", "+err.Error())
|
||||
}
|
||||
} else {
|
||||
result.Data = team
|
||||
}
|
||||
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
}()
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
|
||||
func (s SqlTeamStore) Update(team *model.Team) StoreChannel {
|
||||
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
result := StoreResult{}
|
||||
|
||||
team.PreUpdate()
|
||||
|
||||
if result.Err = team.IsValid(); result.Err != nil {
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
return
|
||||
}
|
||||
|
||||
if oldResult, err := s.GetMaster().Get(model.Team{}, team.Id); err != nil {
|
||||
result.Err = model.NewAppError("SqlTeamStore.Update", "We encounted an error finding the team", "id="+team.Id+", "+err.Error())
|
||||
} else if oldResult == nil {
|
||||
result.Err = model.NewAppError("SqlTeamStore.Update", "We couldn't find the existing team to update", "id="+team.Id)
|
||||
} else {
|
||||
oldTeam := oldResult.(*model.Team)
|
||||
team.CreateAt = oldTeam.CreateAt
|
||||
team.Domain = oldTeam.Domain
|
||||
|
||||
if count, err := s.GetMaster().Update(team); err != nil {
|
||||
result.Err = model.NewAppError("SqlTeamStore.Update", "We encounted an error updating the team", "id="+team.Id+", "+err.Error())
|
||||
} else if count != 1 {
|
||||
result.Err = model.NewAppError("SqlTeamStore.Update", "We couldn't update the team", "id="+team.Id)
|
||||
} else {
|
||||
result.Data = team
|
||||
}
|
||||
}
|
||||
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
}()
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
|
||||
func (s SqlTeamStore) UpdateName(name string, teamId string) StoreChannel {
|
||||
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
result := StoreResult{}
|
||||
|
||||
if _, err := s.GetMaster().Exec("UPDATE Teams SET Name = ? WHERE Id = ?", name, teamId); err != nil {
|
||||
result.Err = model.NewAppError("SqlTeamStore.UpdateName", "We couldn't update the team name", "team_id="+teamId)
|
||||
} else {
|
||||
result.Data = teamId
|
||||
}
|
||||
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
}()
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
|
||||
func (s SqlTeamStore) Get(id string) StoreChannel {
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
result := StoreResult{}
|
||||
|
||||
if obj, err := s.GetReplica().Get(model.Team{}, id); err != nil {
|
||||
result.Err = model.NewAppError("SqlTeamStore.Get", "We encounted an error finding the team", "id="+id+", "+err.Error())
|
||||
} else if obj == nil {
|
||||
result.Err = model.NewAppError("SqlTeamStore.Get", "We couldn't find the existing team", "id="+id)
|
||||
} else {
|
||||
result.Data = obj.(*model.Team)
|
||||
}
|
||||
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
}()
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
|
||||
func (s SqlTeamStore) GetByDomain(domain string) StoreChannel {
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
result := StoreResult{}
|
||||
|
||||
team := model.Team{}
|
||||
|
||||
if err := s.GetReplica().SelectOne(&team, "SELECT * FROM Teams WHERE Domain=?", domain); err != nil {
|
||||
result.Err = model.NewAppError("SqlTeamStore.GetByDomain", "We couldn't find the existing team", "domain="+domain+", "+err.Error())
|
||||
}
|
||||
|
||||
result.Data = &team
|
||||
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
}()
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
|
||||
func (s SqlTeamStore) GetTeamsForEmail(email string) StoreChannel {
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
result := StoreResult{}
|
||||
|
||||
var data []*model.Team
|
||||
if _, err := s.GetReplica().Select(&data, "SELECT Teams.* FROM Teams, Users WHERE Teams.Id = Users.TeamId AND Users.Email = ?", email); err != nil {
|
||||
result.Err = model.NewAppError("SqlTeamStore.GetTeamsForEmail", "We encounted a problem when looking up teams", "email="+email+", "+err.Error())
|
||||
}
|
||||
|
||||
result.Data = data
|
||||
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
}()
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
155
store/sql_team_store_test.go
Обычный файл
155
store/sql_team_store_test.go
Обычный файл
@@ -0,0 +1,155 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package store
|
||||
|
||||
import (
|
||||
"github.com/mattermost/platform/model"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestTeamStoreSave(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
o1 := model.Team{}
|
||||
o1.Name = "Name"
|
||||
o1.Domain = "a" + model.NewId() + "b"
|
||||
o1.Email = model.NewId() + "@nowhere.com"
|
||||
o1.Type = model.TEAM_OPEN
|
||||
|
||||
if err := (<-store.Team().Save(&o1)).Err; err != nil {
|
||||
t.Fatal("couldn't save item", err)
|
||||
}
|
||||
|
||||
if err := (<-store.Team().Save(&o1)).Err; err == nil {
|
||||
t.Fatal("shouldn't be able to update from save")
|
||||
}
|
||||
|
||||
o1.Id = ""
|
||||
if err := (<-store.Team().Save(&o1)).Err; err == nil {
|
||||
t.Fatal("should be unique domain")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTeamStoreUpdate(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
o1 := model.Team{}
|
||||
o1.Name = "Name"
|
||||
o1.Domain = "a" + model.NewId() + "b"
|
||||
o1.Email = model.NewId() + "@nowhere.com"
|
||||
o1.Type = model.TEAM_OPEN
|
||||
<-store.Team().Save(&o1)
|
||||
|
||||
if err := (<-store.Team().Update(&o1)).Err; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
o1.Id = "missing"
|
||||
if err := (<-store.Team().Update(&o1)).Err; err == nil {
|
||||
t.Fatal("Update should have failed because of missing key")
|
||||
}
|
||||
|
||||
o1.Id = model.NewId()
|
||||
if err := (<-store.Team().Update(&o1)).Err; err == nil {
|
||||
t.Fatal("Update should have faile because id change")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTeamStoreUpdateName(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
o1 := &model.Team{}
|
||||
o1.Name = "Name"
|
||||
o1.Domain = "a" + model.NewId() + "b"
|
||||
o1.Email = model.NewId() + "@nowhere.com"
|
||||
o1.Type = model.TEAM_OPEN
|
||||
o1 = (<-store.Team().Save(o1)).Data.(*model.Team)
|
||||
|
||||
newName := "NewName"
|
||||
|
||||
if err := (<-store.Team().UpdateName(newName, o1.Id)).Err; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ro1 := (<-store.Team().Get(o1.Id)).Data.(*model.Team)
|
||||
if ro1.Name != newName {
|
||||
t.Fatal("Name not updated")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTeamStoreGet(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
o1 := model.Team{}
|
||||
o1.Name = "Name"
|
||||
o1.Domain = "a" + model.NewId() + "b"
|
||||
o1.Email = model.NewId() + "@nowhere.com"
|
||||
o1.Type = model.TEAM_OPEN
|
||||
<-store.Team().Save(&o1)
|
||||
|
||||
if r1 := <-store.Team().Get(o1.Id); r1.Err != nil {
|
||||
t.Fatal(r1.Err)
|
||||
} else {
|
||||
if r1.Data.(*model.Team).ToJson() != o1.ToJson() {
|
||||
t.Fatal("invalid returned team")
|
||||
}
|
||||
}
|
||||
|
||||
if err := (<-store.Team().Get("")).Err; err == nil {
|
||||
t.Fatal("Missing id should have failed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTeamStoreGetByDomain(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
o1 := model.Team{}
|
||||
o1.Name = "Name"
|
||||
o1.Domain = "a" + model.NewId() + "b"
|
||||
o1.Email = model.NewId() + "@nowhere.com"
|
||||
o1.Type = model.TEAM_OPEN
|
||||
<-store.Team().Save(&o1)
|
||||
|
||||
if r1 := <-store.Team().GetByDomain(o1.Domain); r1.Err != nil {
|
||||
t.Fatal(r1.Err)
|
||||
} else {
|
||||
if r1.Data.(*model.Team).ToJson() != o1.ToJson() {
|
||||
t.Fatal("invalid returned team")
|
||||
}
|
||||
}
|
||||
|
||||
if err := (<-store.Team().GetByDomain("")).Err; err == nil {
|
||||
t.Fatal("Missing id should have failed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTeamStoreGetForEmail(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
o1 := model.Team{}
|
||||
o1.Name = "Name"
|
||||
o1.Domain = "a" + model.NewId() + "b"
|
||||
o1.Email = model.NewId() + "@nowhere.com"
|
||||
o1.Type = model.TEAM_OPEN
|
||||
<-store.Team().Save(&o1)
|
||||
|
||||
u1 := model.User{}
|
||||
u1.TeamId = o1.Id
|
||||
u1.Email = model.NewId()
|
||||
<-store.User().Save(&u1)
|
||||
|
||||
if r1 := <-store.Team().GetTeamsForEmail(u1.Email); r1.Err != nil {
|
||||
t.Fatal(r1.Err)
|
||||
} else {
|
||||
teams := r1.Data.([]*model.Team)
|
||||
|
||||
if teams[0].Id != o1.Id {
|
||||
t.Fatal("failed to lookup by email")
|
||||
}
|
||||
}
|
||||
|
||||
if r1 := <-store.Team().GetTeamsForEmail("missing"); r1.Err != nil {
|
||||
t.Fatal(r1.Err)
|
||||
}
|
||||
}
|
||||
367
store/sql_user_store.go
Обычный файл
367
store/sql_user_store.go
Обычный файл
@@ -0,0 +1,367 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package store
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/mattermost/platform/model"
|
||||
"github.com/mattermost/platform/utils"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type SqlUserStore struct {
|
||||
*SqlStore
|
||||
}
|
||||
|
||||
func NewSqlUserStore(sqlStore *SqlStore) UserStore {
|
||||
us := &SqlUserStore{sqlStore}
|
||||
|
||||
for _, db := range sqlStore.GetAllConns() {
|
||||
table := db.AddTableWithName(model.User{}, "Users").SetKeys(false, "Id")
|
||||
table.ColMap("Id").SetMaxSize(26)
|
||||
table.ColMap("TeamId").SetMaxSize(26)
|
||||
table.ColMap("Username").SetMaxSize(64)
|
||||
table.ColMap("Password").SetMaxSize(128)
|
||||
table.ColMap("AuthData").SetMaxSize(128)
|
||||
table.ColMap("Email").SetMaxSize(128)
|
||||
table.ColMap("FullName").SetMaxSize(64)
|
||||
table.ColMap("Roles").SetMaxSize(64)
|
||||
table.ColMap("Props").SetMaxSize(4000)
|
||||
table.ColMap("NotifyProps").SetMaxSize(2000)
|
||||
table.SetUniqueTogether("Email", "TeamId")
|
||||
table.SetUniqueTogether("Username", "TeamId")
|
||||
}
|
||||
|
||||
return us
|
||||
}
|
||||
|
||||
func (s SqlUserStore) UpgradeSchemaIfNeeded() {
|
||||
}
|
||||
|
||||
func (us SqlUserStore) CreateIndexesIfNotExists() {
|
||||
us.CreateIndexIfNotExists("idx_team_id", "Users", "TeamId")
|
||||
}
|
||||
|
||||
func (us SqlUserStore) Save(user *model.User) StoreChannel {
|
||||
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
result := StoreResult{}
|
||||
|
||||
if len(user.Id) > 0 {
|
||||
result.Err = model.NewAppError("SqlUserStore.Save", "Must call update for exisiting user", "user_id="+user.Id)
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
return
|
||||
}
|
||||
|
||||
user.PreSave()
|
||||
if result.Err = user.IsValid(); result.Err != nil {
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
return
|
||||
}
|
||||
|
||||
if count, err := us.GetMaster().SelectInt("SELECT COUNT(0) FROM Users WHERE TeamId = ? AND DeleteAt = 0", user.TeamId); err != nil {
|
||||
result.Err = model.NewAppError("SqlUserStore.Save", "Failed to get current team member count", "teamId="+user.TeamId+", "+err.Error())
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
return
|
||||
} else if int(count) > utils.Cfg.TeamSettings.MaxUsersPerTeam {
|
||||
result.Err = model.NewAppError("SqlUserStore.Save", "You've reached the limit of the number of allowed accounts.", "teamId="+user.TeamId)
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
return
|
||||
}
|
||||
|
||||
if err := us.GetMaster().Insert(user); err != nil {
|
||||
if strings.Contains(err.Error(), "Duplicate entry") && strings.Contains(err.Error(), "for key 'Email'") {
|
||||
result.Err = model.NewAppError("SqlUserStore.Save", "An account with that email already exists.", "user_id="+user.Id+", "+err.Error())
|
||||
} else if strings.Contains(err.Error(), "Duplicate entry") && strings.Contains(err.Error(), "for key 'Username'") {
|
||||
result.Err = model.NewAppError("SqlUserStore.Save", "An account with that username already exists.", "user_id="+user.Id+", "+err.Error())
|
||||
} else {
|
||||
result.Err = model.NewAppError("SqlUserStore.Save", "We couldn't save the account.", "user_id="+user.Id+", "+err.Error())
|
||||
}
|
||||
} else {
|
||||
result.Data = user
|
||||
}
|
||||
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
}()
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
|
||||
func (us SqlUserStore) Update(user *model.User, allowRoleActiveUpdate bool) StoreChannel {
|
||||
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
result := StoreResult{}
|
||||
|
||||
user.PreUpdate()
|
||||
|
||||
if result.Err = user.IsValid(); result.Err != nil {
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
return
|
||||
}
|
||||
|
||||
if oldUserResult, err := us.GetMaster().Get(model.User{}, user.Id); err != nil {
|
||||
result.Err = model.NewAppError("SqlUserStore.Update", "We encounted an error finding the account", "user_id="+user.Id+", "+err.Error())
|
||||
} else if oldUserResult == nil {
|
||||
result.Err = model.NewAppError("SqlUserStore.Update", "We couldn't find the existing account to update", "user_id="+user.Id)
|
||||
} else {
|
||||
oldUser := oldUserResult.(*model.User)
|
||||
user.CreateAt = oldUser.CreateAt
|
||||
user.AuthData = oldUser.AuthData
|
||||
user.Password = oldUser.Password
|
||||
user.LastPasswordUpdate = oldUser.LastPasswordUpdate
|
||||
user.TeamId = oldUser.TeamId
|
||||
user.LastActivityAt = oldUser.LastActivityAt
|
||||
user.LastPingAt = oldUser.LastPingAt
|
||||
user.EmailVerified = oldUser.EmailVerified
|
||||
|
||||
if !allowRoleActiveUpdate {
|
||||
user.Roles = oldUser.Roles
|
||||
user.DeleteAt = oldUser.DeleteAt
|
||||
}
|
||||
|
||||
if user.Email != oldUser.Email {
|
||||
user.EmailVerified = false
|
||||
}
|
||||
|
||||
if count, err := us.GetMaster().Update(user); err != nil {
|
||||
result.Err = model.NewAppError("SqlUserStore.Update", "We encounted an error updating the account", "user_id="+user.Id+", "+err.Error())
|
||||
} else if count != 1 {
|
||||
result.Err = model.NewAppError("SqlUserStore.Update", "We couldn't update the account", "user_id="+user.Id)
|
||||
} else {
|
||||
result.Data = [2]*model.User{user, oldUser}
|
||||
}
|
||||
}
|
||||
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
}()
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
|
||||
func (us SqlUserStore) UpdateLastPingAt(userId string, time int64) StoreChannel {
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
result := StoreResult{}
|
||||
|
||||
if _, err := us.GetMaster().Exec("UPDATE Users SET LastPingAt = ? WHERE Id = ?", time, userId); err != nil {
|
||||
result.Err = model.NewAppError("SqlUserStore.UpdateLastPingAt", "We couldn't update the last_ping_at", "user_id="+userId)
|
||||
} else {
|
||||
result.Data = userId
|
||||
}
|
||||
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
}()
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
|
||||
func (us SqlUserStore) UpdateLastActivityAt(userId string, time int64) StoreChannel {
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
result := StoreResult{}
|
||||
|
||||
if _, err := us.GetMaster().Exec("UPDATE Users SET LastActivityAt = ? WHERE Id = ?", time, userId); err != nil {
|
||||
result.Err = model.NewAppError("SqlUserStore.UpdateLastActivityAt", "We couldn't update the last_activity_at", "user_id="+userId)
|
||||
} else {
|
||||
result.Data = userId
|
||||
}
|
||||
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
}()
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
|
||||
func (us SqlUserStore) UpdateUserAndSessionActivity(userId string, sessionId string, time int64) StoreChannel {
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
result := StoreResult{}
|
||||
|
||||
if _, err := us.GetMaster().Exec("UPDATE Sessions, Users SET Users.LastActivityAt = ?, Sessions.LastActivityAt = ? WHERE Users.Id = ? AND Sessions.Id = ?", time, time, userId, sessionId); err != nil {
|
||||
result.Err = model.NewAppError("SqlUserStore.UpdateLastActivityAt", "We couldn't update the last_activity_at", "user_id="+userId+" session_id="+sessionId+" err="+err.Error())
|
||||
} else {
|
||||
result.Data = userId
|
||||
}
|
||||
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
}()
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
|
||||
func (us SqlUserStore) UpdatePassword(userId, hashedPassword string) StoreChannel {
|
||||
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
result := StoreResult{}
|
||||
|
||||
updateAt := model.GetMillis()
|
||||
|
||||
if _, err := us.GetMaster().Exec("UPDATE Users SET Password = ?, LastPasswordUpdate = ?, UpdateAt = ? WHERE Id = ?", hashedPassword, updateAt, updateAt, userId); err != nil {
|
||||
result.Err = model.NewAppError("SqlUserStore.UpdatePassword", "We couldn't update the user password", "id="+userId+", "+err.Error())
|
||||
} else {
|
||||
result.Data = userId
|
||||
}
|
||||
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
}()
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
|
||||
func (us SqlUserStore) Get(id string) StoreChannel {
|
||||
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
result := StoreResult{}
|
||||
|
||||
if obj, err := us.GetReplica().Get(model.User{}, id); err != nil {
|
||||
result.Err = model.NewAppError("SqlUserStore.Get", "We encounted an error finding the account", "user_id="+id+", "+err.Error())
|
||||
} else if obj == nil {
|
||||
result.Err = model.NewAppError("SqlUserStore.Get", "We couldn't find the existing account", "user_id="+id)
|
||||
} else {
|
||||
result.Data = obj.(*model.User)
|
||||
}
|
||||
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
|
||||
}()
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
|
||||
func (s SqlUserStore) GetEtagForProfiles(teamId string) StoreChannel {
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
result := StoreResult{}
|
||||
|
||||
updateAt, err := s.GetReplica().SelectInt("SELECT UpdateAt FROM Users WHERE TeamId = ? ORDER BY UpdateAt DESC LIMIT 1", teamId)
|
||||
if err != nil {
|
||||
result.Data = fmt.Sprintf("%v.%v", model.ETAG_ROOT_VERSION, model.GetMillis())
|
||||
} else {
|
||||
result.Data = fmt.Sprintf("%v.%v", model.ETAG_ROOT_VERSION, updateAt)
|
||||
}
|
||||
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
}()
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
|
||||
func (us SqlUserStore) GetProfiles(teamId string) StoreChannel {
|
||||
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
result := StoreResult{}
|
||||
|
||||
var users []*model.User
|
||||
|
||||
if _, err := us.GetReplica().Select(&users, "SELECT * FROM Users WHERE TeamId = ?", teamId); err != nil {
|
||||
result.Err = model.NewAppError("SqlUserStore.GetProfiles", "We encounted an error while finding user profiles", err.Error())
|
||||
} else {
|
||||
|
||||
userMap := make(map[string]*model.User)
|
||||
|
||||
for _, u := range users {
|
||||
u.Password = ""
|
||||
u.AuthData = ""
|
||||
userMap[u.Id] = u
|
||||
}
|
||||
|
||||
result.Data = userMap
|
||||
}
|
||||
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
}()
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
|
||||
func (us SqlUserStore) GetByEmail(teamId string, email string) StoreChannel {
|
||||
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
result := StoreResult{}
|
||||
|
||||
user := model.User{}
|
||||
|
||||
if err := us.GetReplica().SelectOne(&user, "SELECT * FROM Users WHERE TeamId=? AND Email=?", teamId, email); err != nil {
|
||||
result.Err = model.NewAppError("SqlUserStore.GetByEmail", "We couldn't find the existing account", "teamId="+teamId+", email="+email+", "+err.Error())
|
||||
}
|
||||
|
||||
result.Data = &user
|
||||
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
}()
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
|
||||
func (us SqlUserStore) GetByUsername(teamId string, username string) StoreChannel {
|
||||
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
result := StoreResult{}
|
||||
|
||||
user := model.User{}
|
||||
|
||||
if err := us.GetReplica().SelectOne(&user, "SELECT * FROM Users WHERE TeamId=? AND Username=?", teamId, username); err != nil {
|
||||
result.Err = model.NewAppError("SqlUserStore.GetByUsername", "We couldn't find the existing account", "teamId="+teamId+", username="+username+", "+err.Error())
|
||||
}
|
||||
|
||||
result.Data = &user
|
||||
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
}()
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
|
||||
func (us SqlUserStore) VerifyEmail(userId string) StoreChannel {
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
result := StoreResult{}
|
||||
|
||||
if _, err := us.GetMaster().Exec("UPDATE Users SET EmailVerified = 1 WHERE Id = ?", userId); err != nil {
|
||||
result.Err = model.NewAppError("SqlUserStore.VerifyEmail", "Unable to update verify email field", "userId="+userId+", "+err.Error())
|
||||
}
|
||||
|
||||
result.Data = userId
|
||||
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
}()
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
276
store/sql_user_store_test.go
Обычный файл
276
store/sql_user_store_test.go
Обычный файл
@@ -0,0 +1,276 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package store
|
||||
|
||||
import (
|
||||
"github.com/mattermost/platform/model"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestUserStoreSave(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
u1 := model.User{}
|
||||
u1.Email = model.NewId()
|
||||
u1.Username = model.NewId()
|
||||
u1.TeamId = model.NewId()
|
||||
|
||||
if err := (<-store.User().Save(&u1)).Err; err != nil {
|
||||
t.Fatal("couldn't save user", err)
|
||||
}
|
||||
|
||||
if err := (<-store.User().Save(&u1)).Err; err == nil {
|
||||
t.Fatal("shouldn't be able to update user from save")
|
||||
}
|
||||
|
||||
u1.Id = ""
|
||||
if err := (<-store.User().Save(&u1)).Err; err == nil {
|
||||
t.Fatal("should be unique email")
|
||||
}
|
||||
|
||||
u1.Email = ""
|
||||
if err := (<-store.User().Save(&u1)).Err; err == nil {
|
||||
t.Fatal("should be unique username")
|
||||
}
|
||||
|
||||
u1.Email = strings.Repeat("0123456789", 20)
|
||||
u1.Username = ""
|
||||
if err := (<-store.User().Save(&u1)).Err; err == nil {
|
||||
t.Fatal("should be unique username")
|
||||
}
|
||||
|
||||
for i := 0; i < 150; i++ {
|
||||
u1.Id = ""
|
||||
u1.Email = model.NewId()
|
||||
u1.Username = model.NewId()
|
||||
if err := (<-store.User().Save(&u1)).Err; err != nil {
|
||||
t.Fatal("couldn't save item", err)
|
||||
}
|
||||
}
|
||||
|
||||
u1.Id = ""
|
||||
u1.Email = model.NewId()
|
||||
u1.Username = model.NewId()
|
||||
if err := (<-store.User().Save(&u1)).Err; err == nil {
|
||||
t.Fatal("should be the limit", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUserStoreUpdate(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
u1 := model.User{}
|
||||
u1.TeamId = model.NewId()
|
||||
u1.Email = model.NewId()
|
||||
<-store.User().Save(&u1)
|
||||
|
||||
if err := (<-store.User().Update(&u1, false)).Err; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
u1.Id = "missing"
|
||||
if err := (<-store.User().Update(&u1, false)).Err; err == nil {
|
||||
t.Fatal("Update should have failed because of missing key")
|
||||
}
|
||||
|
||||
u1.Id = model.NewId()
|
||||
if err := (<-store.User().Update(&u1, false)).Err; err == nil {
|
||||
t.Fatal("Update should have faile because id change")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUserStoreUpdateLastPingAt(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
u1 := model.User{}
|
||||
u1.TeamId = model.NewId()
|
||||
u1.Email = model.NewId()
|
||||
<-store.User().Save(&u1)
|
||||
|
||||
if err := (<-store.User().UpdateLastPingAt(u1.Id, 1234567890)).Err; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if r1 := <-store.User().Get(u1.Id); r1.Err != nil {
|
||||
t.Fatal(r1.Err)
|
||||
} else {
|
||||
if r1.Data.(*model.User).LastPingAt != 1234567890 {
|
||||
t.Fatal("LastPingAt not updated correctly")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestUserStoreUpdateLastActivityAt(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
u1 := model.User{}
|
||||
u1.TeamId = model.NewId()
|
||||
u1.Email = model.NewId()
|
||||
<-store.User().Save(&u1)
|
||||
|
||||
if err := (<-store.User().UpdateLastActivityAt(u1.Id, 1234567890)).Err; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if r1 := <-store.User().Get(u1.Id); r1.Err != nil {
|
||||
t.Fatal(r1.Err)
|
||||
} else {
|
||||
if r1.Data.(*model.User).LastActivityAt != 1234567890 {
|
||||
t.Fatal("LastActivityAt not updated correctly")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestUserStoreUpdateUserAndSessionActivity(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
u1 := model.User{}
|
||||
u1.TeamId = model.NewId()
|
||||
u1.Email = model.NewId()
|
||||
<-store.User().Save(&u1)
|
||||
|
||||
s1 := model.Session{}
|
||||
s1.UserId = u1.Id
|
||||
s1.TeamId = u1.TeamId
|
||||
<-store.Session().Save(&s1)
|
||||
|
||||
if err := (<-store.User().UpdateUserAndSessionActivity(u1.Id, s1.Id, 1234567890)).Err; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if r1 := <-store.User().Get(u1.Id); r1.Err != nil {
|
||||
t.Fatal(r1.Err)
|
||||
} else {
|
||||
if r1.Data.(*model.User).LastActivityAt != 1234567890 {
|
||||
t.Fatal("LastActivityAt not updated correctly for user")
|
||||
}
|
||||
}
|
||||
|
||||
if r2 := <-store.Session().Get(s1.Id); r2.Err != nil {
|
||||
t.Fatal(r2.Err)
|
||||
} else {
|
||||
if r2.Data.(*model.Session).LastActivityAt != 1234567890 {
|
||||
t.Fatal("LastActivityAt not updated correctly for session")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestUserStoreGet(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
u1 := model.User{}
|
||||
u1.TeamId = model.NewId()
|
||||
u1.Email = model.NewId()
|
||||
<-store.User().Save(&u1)
|
||||
|
||||
if r1 := <-store.User().Get(u1.Id); r1.Err != nil {
|
||||
t.Fatal(r1.Err)
|
||||
} else {
|
||||
if r1.Data.(*model.User).ToJson() != u1.ToJson() {
|
||||
t.Fatal("invalid returned user")
|
||||
}
|
||||
}
|
||||
|
||||
if err := (<-store.User().Get("")).Err; err == nil {
|
||||
t.Fatal("Missing id should have failed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUserStoreGetProfiles(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
u1 := model.User{}
|
||||
u1.TeamId = model.NewId()
|
||||
u1.Email = model.NewId()
|
||||
<-store.User().Save(&u1)
|
||||
|
||||
u2 := model.User{}
|
||||
u2.TeamId = u1.TeamId
|
||||
u2.Email = model.NewId()
|
||||
<-store.User().Save(&u2)
|
||||
|
||||
if r1 := <-store.User().GetProfiles(u1.TeamId); r1.Err != nil {
|
||||
t.Fatal(r1.Err)
|
||||
} else {
|
||||
users := r1.Data.(map[string]*model.User)
|
||||
if len(users) != 2 {
|
||||
t.Fatal("invalid returned users")
|
||||
}
|
||||
|
||||
if users[u1.Id].Id != u1.Id {
|
||||
t.Fatal("invalid returned user")
|
||||
}
|
||||
}
|
||||
|
||||
if r2 := <-store.User().GetProfiles("123"); r2.Err != nil {
|
||||
t.Fatal(r2.Err)
|
||||
} else {
|
||||
if len(r2.Data.(map[string]*model.User)) != 0 {
|
||||
t.Fatal("should have returned empty map")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestUserStoreGetByEmail(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
u1 := model.User{}
|
||||
u1.TeamId = model.NewId()
|
||||
u1.Email = model.NewId()
|
||||
<-store.User().Save(&u1)
|
||||
|
||||
if err := (<-store.User().GetByEmail(u1.TeamId, u1.Email)).Err; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := (<-store.User().GetByEmail("", "")).Err; err == nil {
|
||||
t.Fatal("Should have failed because of missing email")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUserStoreGetByUsername(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
u1 := model.User{}
|
||||
u1.TeamId = model.NewId()
|
||||
u1.Email = model.NewId()
|
||||
u1.Username = model.NewId()
|
||||
<-store.User().Save(&u1)
|
||||
|
||||
if err := (<-store.User().GetByUsername(u1.TeamId, u1.Username)).Err; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := (<-store.User().GetByUsername("", "")).Err; err == nil {
|
||||
t.Fatal("Should have failed because of missing username")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUserStoreUpdatePassword(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
u1 := model.User{}
|
||||
u1.TeamId = model.NewId()
|
||||
u1.Email = model.NewId()
|
||||
<-store.User().Save(&u1)
|
||||
|
||||
hashedPassword := model.HashPassword("newpwd")
|
||||
|
||||
if err := (<-store.User().UpdatePassword(u1.Id, hashedPassword)).Err; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if r1 := <-store.User().GetByEmail(u1.TeamId, u1.Email); r1.Err != nil {
|
||||
t.Fatal(r1.Err)
|
||||
} else {
|
||||
user := r1.Data.(*model.User)
|
||||
if user.Password != hashedPassword {
|
||||
t.Fatal("Password was not updated correctly")
|
||||
}
|
||||
}
|
||||
}
|
||||
94
store/store.go
Обычный файл
94
store/store.go
Обычный файл
@@ -0,0 +1,94 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package store
|
||||
|
||||
import (
|
||||
"github.com/mattermost/platform/model"
|
||||
)
|
||||
|
||||
type StoreResult struct {
|
||||
Data interface{}
|
||||
Err *model.AppError
|
||||
}
|
||||
|
||||
type StoreChannel chan StoreResult
|
||||
|
||||
type Store interface {
|
||||
Team() TeamStore
|
||||
Channel() ChannelStore
|
||||
Post() PostStore
|
||||
User() UserStore
|
||||
Audit() AuditStore
|
||||
Session() SessionStore
|
||||
Close()
|
||||
}
|
||||
|
||||
type TeamStore interface {
|
||||
Save(team *model.Team) StoreChannel
|
||||
Update(team *model.Team) StoreChannel
|
||||
UpdateName(name string, teamId string) StoreChannel
|
||||
Get(id string) StoreChannel
|
||||
GetByDomain(domain string) StoreChannel
|
||||
GetTeamsForEmail(domain string) StoreChannel
|
||||
}
|
||||
|
||||
type ChannelStore interface {
|
||||
Save(channel *model.Channel) StoreChannel
|
||||
Update(channel *model.Channel) StoreChannel
|
||||
Get(id string) StoreChannel
|
||||
Delete(channelId string, time int64) StoreChannel
|
||||
GetByName(team_id string, domain string) StoreChannel
|
||||
GetChannels(teamId string, userId string) StoreChannel
|
||||
GetMoreChannels(teamId string, userId string) StoreChannel
|
||||
|
||||
SaveMember(member *model.ChannelMember) StoreChannel
|
||||
GetMembers(channelId string) StoreChannel
|
||||
GetMember(channelId string, userId string) StoreChannel
|
||||
RemoveMember(channelId string, userId string) StoreChannel
|
||||
GetExtraMembers(channelId string, limit int) StoreChannel
|
||||
CheckPermissionsTo(teamId string, channelId string, userId string) StoreChannel
|
||||
CheckOpenChannelPermissions(teamId string, channelId string) StoreChannel
|
||||
CheckPermissionsToByName(teamId string, channelName string, userId string) StoreChannel
|
||||
UpdateLastViewedAt(channelId string, userId string) StoreChannel
|
||||
IncrementMentionCount(channelId string, userId string) StoreChannel
|
||||
UpdateNotifyLevel(channelId string, userId string, notifyLevel string) StoreChannel
|
||||
}
|
||||
|
||||
type PostStore interface {
|
||||
Save(post *model.Post) StoreChannel
|
||||
Update(post *model.Post, newMessage string, newHashtags string) StoreChannel
|
||||
Get(id string) StoreChannel
|
||||
Delete(postId string, time int64) StoreChannel
|
||||
GetPosts(channelId string, offset int, limit int) StoreChannel
|
||||
GetEtag(channelId string) StoreChannel
|
||||
Search(teamId string, userId string, terms string, isHashtagSearch bool) StoreChannel
|
||||
}
|
||||
|
||||
type UserStore interface {
|
||||
Save(user *model.User) StoreChannel
|
||||
Update(user *model.User, allowRoleUpdate bool) StoreChannel
|
||||
UpdateLastPingAt(userId string, time int64) StoreChannel
|
||||
UpdateLastActivityAt(userId string, time int64) StoreChannel
|
||||
UpdateUserAndSessionActivity(userId string, sessionId string, time int64) StoreChannel
|
||||
UpdatePassword(userId, newPassword string) StoreChannel
|
||||
Get(id string) StoreChannel
|
||||
GetProfiles(teamId string) StoreChannel
|
||||
GetByEmail(teamId string, email string) StoreChannel
|
||||
GetByUsername(teamId string, username string) StoreChannel
|
||||
VerifyEmail(userId string) StoreChannel
|
||||
GetEtagForProfiles(teamId string) StoreChannel
|
||||
}
|
||||
|
||||
type SessionStore interface {
|
||||
Save(session *model.Session) StoreChannel
|
||||
Get(id string) StoreChannel
|
||||
GetSessions(userId string) StoreChannel
|
||||
Remove(sessionIdOrAlt string) StoreChannel
|
||||
UpdateLastActivityAt(sessionId string, time int64) StoreChannel
|
||||
}
|
||||
|
||||
type AuditStore interface {
|
||||
Save(audit *model.Audit) StoreChannel
|
||||
Get(user_id string, limit int) StoreChannel
|
||||
}
|
||||
Ссылка в новой задаче
Block a user