first commit
Этот коммит содержится в:
61
api/api.go
Обычный файл
61
api/api.go
Обычный файл
@@ -0,0 +1,61 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
l4g "code.google.com/p/log4go"
|
||||
"github.com/mattermost/platform/model"
|
||||
"github.com/mattermost/platform/utils"
|
||||
"html/template"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
var ServerTemplates *template.Template
|
||||
|
||||
type ServerTemplatePage Page
|
||||
|
||||
func NewServerTemplatePage(templateName, teamUrl string) *ServerTemplatePage {
|
||||
props := make(map[string]string)
|
||||
props["AnalyticsUrl"] = utils.Cfg.ServiceSettings.AnalyticsUrl
|
||||
return &ServerTemplatePage{TemplateName: templateName, SiteName: utils.Cfg.ServiceSettings.SiteName, FeedbackEmail: utils.Cfg.EmailSettings.FeedbackEmail, TeamUrl: teamUrl, Props: props}
|
||||
}
|
||||
|
||||
func (me *ServerTemplatePage) Render() string {
|
||||
var text bytes.Buffer
|
||||
if err := ServerTemplates.ExecuteTemplate(&text, me.TemplateName, me); err != nil {
|
||||
l4g.Error("Error rendering template %v err=%v", me.TemplateName, err)
|
||||
}
|
||||
|
||||
return text.String()
|
||||
}
|
||||
|
||||
func InitApi() {
|
||||
r := Srv.Router.PathPrefix("/api/v1").Subrouter()
|
||||
InitUser(r)
|
||||
InitTeam(r)
|
||||
InitChannel(r)
|
||||
InitPost(r)
|
||||
InitWebSocket(r)
|
||||
InitFile(r)
|
||||
InitCommand(r)
|
||||
|
||||
templatesDir := utils.FindDir("api/templates")
|
||||
l4g.Debug("Parsing server templates at %v", templatesDir)
|
||||
var err error
|
||||
if ServerTemplates, err = template.ParseGlob(templatesDir + "*.html"); err != nil {
|
||||
l4g.Error("Failed to parse server templates %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func HandleEtag(etag string, w http.ResponseWriter, r *http.Request) bool {
|
||||
if et := r.Header.Get(model.HEADER_ETAG_CLIENT); len(etag) > 0 {
|
||||
if et == etag {
|
||||
w.WriteHeader(http.StatusNotModified)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
42
api/api_test.go
Обычный файл
42
api/api_test.go
Обычный файл
@@ -0,0 +1,42 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/mattermost/platform/model"
|
||||
"github.com/mattermost/platform/utils"
|
||||
)
|
||||
|
||||
var Client *model.Client
|
||||
|
||||
func Setup() {
|
||||
if Srv == nil {
|
||||
utils.LoadConfig("config.json")
|
||||
NewServer()
|
||||
StartServer()
|
||||
InitApi()
|
||||
Client = model.NewClient("http://localhost:" + utils.Cfg.ServiceSettings.Port + "/api/v1")
|
||||
}
|
||||
}
|
||||
|
||||
func SetupBenchmark() (*model.Team, *model.User, *model.Channel) {
|
||||
Setup()
|
||||
|
||||
team := &model.Team{Name: "Benchmark Team", Domain: "z-z-" + model.NewId() + "a", Email: "benchmark@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
user := &model.User{TeamId: team.Id, Email: model.NewId() + "benchmark@test.com", FullName: "Mr. Benchmarker", Password: "pwd"}
|
||||
user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(user.Id)
|
||||
Client.LoginByEmail(team.Domain, user.Email, "pwd")
|
||||
channel := &model.Channel{DisplayName: "Benchmark Channel", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channel = Client.Must(Client.CreateChannel(channel)).Data.(*model.Channel)
|
||||
|
||||
return team, user, channel
|
||||
}
|
||||
|
||||
func TearDown() {
|
||||
if Srv != nil {
|
||||
StopServer()
|
||||
}
|
||||
}
|
||||
70
api/auto_channels.go
Обычный файл
70
api/auto_channels.go
Обычный файл
@@ -0,0 +1,70 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/mattermost/platform/model"
|
||||
"github.com/mattermost/platform/utils"
|
||||
)
|
||||
|
||||
type AutoChannelCreator struct {
|
||||
client *model.Client
|
||||
teamID string
|
||||
Fuzzy bool
|
||||
DisplayNameLen utils.Range
|
||||
DisplayNameCharset string
|
||||
NameLen utils.Range
|
||||
NameCharset string
|
||||
ChannelType string
|
||||
}
|
||||
|
||||
func NewAutoChannelCreator(client *model.Client, teamID string) *AutoChannelCreator {
|
||||
return &AutoChannelCreator{
|
||||
client: client,
|
||||
teamID: teamID,
|
||||
Fuzzy: false,
|
||||
DisplayNameLen: CHANNEL_DISPLAY_NAME_LEN,
|
||||
DisplayNameCharset: utils.ALPHANUMERIC,
|
||||
NameLen: CHANNEL_NAME_LEN,
|
||||
NameCharset: utils.LOWERCASE,
|
||||
ChannelType: CHANNEL_TYPE,
|
||||
}
|
||||
}
|
||||
|
||||
func (cfg *AutoChannelCreator) createRandomChannel() (*model.Channel, bool) {
|
||||
var displayName string
|
||||
if cfg.Fuzzy {
|
||||
displayName = utils.FuzzName()
|
||||
} else {
|
||||
displayName = utils.RandomName(cfg.NameLen, cfg.NameCharset)
|
||||
}
|
||||
name := utils.RandomName(cfg.NameLen, cfg.NameCharset)
|
||||
|
||||
channel := &model.Channel{
|
||||
TeamId: cfg.teamID,
|
||||
DisplayName: displayName,
|
||||
Name: name,
|
||||
Type: cfg.ChannelType}
|
||||
|
||||
result, err := cfg.client.CreateChannel(channel)
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
return result.Data.(*model.Channel), true
|
||||
}
|
||||
|
||||
func (cfg *AutoChannelCreator) CreateTestChannels(num utils.Range) ([]*model.Channel, bool) {
|
||||
numChannels := utils.RandIntFromRange(num)
|
||||
channels := make([]*model.Channel, numChannels)
|
||||
|
||||
for i := 0; i < numChannels; i++ {
|
||||
var err bool
|
||||
channels[i], err = cfg.createRandomChannel()
|
||||
if err != true {
|
||||
return channels, false
|
||||
}
|
||||
}
|
||||
|
||||
return channels, true
|
||||
}
|
||||
36
api/auto_constants.go
Обычный файл
36
api/auto_constants.go
Обычный файл
@@ -0,0 +1,36 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/mattermost/platform/model"
|
||||
"github.com/mattermost/platform/utils"
|
||||
)
|
||||
|
||||
const (
|
||||
USER_PASSWORD = "passwd"
|
||||
CHANNEL_TYPE = model.CHANNEL_OPEN
|
||||
FUZZ_USER_EMAIL_PREFIX_LEN = 10
|
||||
BTEST_TEAM_NAME = "TestTeam"
|
||||
BTEST_TEAM_DOMAIN_NAME = "z-z-testdomaina"
|
||||
BTEST_TEAM_EMAIL = "test@nowhere.com"
|
||||
BTEST_TEAM_TYPE = model.TEAM_OPEN
|
||||
BTEST_USER_NAME = "Mr. Testing Tester"
|
||||
BTEST_USER_EMAIL = "success+ttester@simulator.amazonses.com"
|
||||
BTEST_USER_PASSWORD = "passwd"
|
||||
)
|
||||
|
||||
var (
|
||||
TEAM_NAME_LEN = utils.Range{10, 20}
|
||||
TEAM_DOMAIN_NAME_LEN = utils.Range{10, 20}
|
||||
TEAM_EMAIL_LEN = utils.Range{15, 30}
|
||||
USER_NAME_LEN = utils.Range{5, 20}
|
||||
USER_EMAIL_LEN = utils.Range{15, 30}
|
||||
CHANNEL_DISPLAY_NAME_LEN = utils.Range{10, 20}
|
||||
CHANNEL_NAME_LEN = utils.Range{5, 20}
|
||||
POST_MESSAGE_LEN = utils.Range{100, 400}
|
||||
POST_HASHTAGS_NUM = utils.Range{5, 10}
|
||||
POST_MENTIONS_NUM = utils.Range{0, 3}
|
||||
TEST_IMAGE_FILENAMES = []string{"test.png", "salamander.jpg", "toothless.gif"}
|
||||
)
|
||||
97
api/auto_enviroment.go
Обычный файл
97
api/auto_enviroment.go
Обычный файл
@@ -0,0 +1,97 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/mattermost/platform/model"
|
||||
"github.com/mattermost/platform/utils"
|
||||
"math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
type TestEnviroment struct {
|
||||
Teams []*model.Team
|
||||
Enviroments []TeamEnviroment
|
||||
}
|
||||
|
||||
func CreateTestEnviromentWithTeams(client *model.Client, rangeTeams utils.Range, rangeChannels utils.Range, rangeUsers utils.Range, rangePosts utils.Range, fuzzy bool) (TestEnviroment, bool) {
|
||||
rand.Seed(time.Now().UTC().UnixNano())
|
||||
|
||||
teamCreator := NewAutoTeamCreator(client)
|
||||
teamCreator.Fuzzy = fuzzy
|
||||
teams, err := teamCreator.CreateTestTeams(rangeTeams)
|
||||
if err != true {
|
||||
return TestEnviroment{}, false
|
||||
}
|
||||
|
||||
enviroment := TestEnviroment{teams, make([]TeamEnviroment, len(teams))}
|
||||
|
||||
for i, team := range teams {
|
||||
userCreator := NewAutoUserCreator(client, team.Id)
|
||||
userCreator.Fuzzy = fuzzy
|
||||
randomUser, err := userCreator.createRandomUser()
|
||||
if err != true {
|
||||
return TestEnviroment{}, false
|
||||
}
|
||||
client.LoginById(randomUser.Id, USER_PASSWORD)
|
||||
teamEnviroment, err := CreateTestEnviromentInTeam(client, team.Id, rangeChannels, rangeUsers, rangePosts, fuzzy)
|
||||
if err != true {
|
||||
return TestEnviroment{}, false
|
||||
}
|
||||
enviroment.Enviroments[i] = teamEnviroment
|
||||
}
|
||||
|
||||
return enviroment, true
|
||||
}
|
||||
|
||||
func CreateTestEnviromentInTeam(client *model.Client, teamID string, rangeChannels utils.Range, rangeUsers utils.Range, rangePosts utils.Range, fuzzy bool) (TeamEnviroment, bool) {
|
||||
rand.Seed(time.Now().UTC().UnixNano())
|
||||
|
||||
// We need to create at least one user
|
||||
if rangeUsers.Begin <= 0 {
|
||||
rangeUsers.Begin = 1
|
||||
}
|
||||
|
||||
userCreator := NewAutoUserCreator(client, teamID)
|
||||
userCreator.Fuzzy = fuzzy
|
||||
users, err := userCreator.CreateTestUsers(rangeUsers)
|
||||
if err != true {
|
||||
return TeamEnviroment{}, false
|
||||
}
|
||||
usernames := make([]string, len(users))
|
||||
for i, user := range users {
|
||||
usernames[i] = user.Username
|
||||
}
|
||||
|
||||
channelCreator := NewAutoChannelCreator(client, teamID)
|
||||
channelCreator.Fuzzy = fuzzy
|
||||
channels, err := channelCreator.CreateTestChannels(rangeChannels)
|
||||
|
||||
// Have every user join every channel
|
||||
for _, user := range users {
|
||||
for _, channel := range channels {
|
||||
client.LoginById(user.Id, USER_PASSWORD)
|
||||
client.JoinChannel(channel.Id)
|
||||
}
|
||||
}
|
||||
|
||||
if err != true {
|
||||
return TeamEnviroment{}, false
|
||||
}
|
||||
numPosts := utils.RandIntFromRange(rangePosts)
|
||||
numImages := utils.RandIntFromRange(rangePosts) / 4
|
||||
for j := 0; j < numPosts; j++ {
|
||||
user := users[utils.RandIntFromRange(utils.Range{0, len(users) - 1})]
|
||||
client.LoginById(user.Id, USER_PASSWORD)
|
||||
for i, channel := range channels {
|
||||
postCreator := NewAutoPostCreator(client, channel.Id)
|
||||
postCreator.HasImage = i < numImages
|
||||
postCreator.Users = usernames
|
||||
postCreator.Fuzzy = fuzzy
|
||||
postCreator.CreateRandomPost()
|
||||
}
|
||||
}
|
||||
|
||||
return TeamEnviroment{users, channels}, true
|
||||
}
|
||||
126
api/auto_posts.go
Обычный файл
126
api/auto_posts.go
Обычный файл
@@ -0,0 +1,126 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/mattermost/platform/model"
|
||||
"github.com/mattermost/platform/utils"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"os"
|
||||
)
|
||||
|
||||
type AutoPostCreator struct {
|
||||
client *model.Client
|
||||
channelid string
|
||||
Fuzzy bool
|
||||
TextLength utils.Range
|
||||
HasImage bool
|
||||
ImageFilenames []string
|
||||
Users []string
|
||||
Mentions utils.Range
|
||||
Tags utils.Range
|
||||
}
|
||||
|
||||
// Automatic poster used for testing
|
||||
func NewAutoPostCreator(client *model.Client, channelid string) *AutoPostCreator {
|
||||
return &AutoPostCreator{
|
||||
client: client,
|
||||
channelid: channelid,
|
||||
Fuzzy: false,
|
||||
TextLength: utils.Range{100, 200},
|
||||
HasImage: false,
|
||||
ImageFilenames: TEST_IMAGE_FILENAMES,
|
||||
Users: []string{},
|
||||
Mentions: utils.Range{0, 5},
|
||||
Tags: utils.Range{0, 7},
|
||||
}
|
||||
}
|
||||
|
||||
func (cfg *AutoPostCreator) UploadTestFile() ([]string, bool) {
|
||||
body := &bytes.Buffer{}
|
||||
writer := multipart.NewWriter(body)
|
||||
|
||||
filename := cfg.ImageFilenames[utils.RandIntFromRange(utils.Range{0, len(cfg.ImageFilenames) - 1})]
|
||||
|
||||
part, err := writer.CreateFormFile("files", filename)
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
path := utils.FindDir("web/static/images")
|
||||
file, err := os.Open(path + "/" + filename)
|
||||
defer file.Close()
|
||||
|
||||
_, err = io.Copy(part, file)
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
field, err := writer.CreateFormField("channel_id")
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
_, err = field.Write([]byte(cfg.channelid))
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
err = writer.Close()
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
resp, appErr := cfg.client.UploadFile("/files/upload", body.Bytes(), writer.FormDataContentType())
|
||||
if appErr != nil {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
return resp.Data.(*model.FileUploadResponse).Filenames, true
|
||||
}
|
||||
|
||||
func (cfg *AutoPostCreator) CreateRandomPost() (*model.Post, bool) {
|
||||
var filenames []string
|
||||
if cfg.HasImage {
|
||||
var err1 bool
|
||||
filenames, err1 = cfg.UploadTestFile()
|
||||
if err1 == false {
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
|
||||
var postText string
|
||||
if cfg.Fuzzy {
|
||||
postText = utils.FuzzPost()
|
||||
} else {
|
||||
postText = utils.RandomText(cfg.TextLength, cfg.Tags, cfg.Mentions, cfg.Users)
|
||||
}
|
||||
|
||||
post := &model.Post{
|
||||
ChannelId: cfg.channelid,
|
||||
Message: postText,
|
||||
Filenames: filenames}
|
||||
result, err2 := cfg.client.CreatePost(post)
|
||||
if err2 != nil {
|
||||
return nil, false
|
||||
}
|
||||
return result.Data.(*model.Post), true
|
||||
}
|
||||
|
||||
func (cfg *AutoPostCreator) CreateTestPosts(rangePosts utils.Range) ([]*model.Post, bool) {
|
||||
numPosts := utils.RandIntFromRange(rangePosts)
|
||||
posts := make([]*model.Post, numPosts)
|
||||
|
||||
for i := 0; i < numPosts; i++ {
|
||||
var err bool
|
||||
posts[i], err = cfg.CreateRandomPost()
|
||||
if err != true {
|
||||
return posts, false
|
||||
}
|
||||
}
|
||||
|
||||
return posts, true
|
||||
}
|
||||
81
api/auto_teams.go
Обычный файл
81
api/auto_teams.go
Обычный файл
@@ -0,0 +1,81 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/mattermost/platform/model"
|
||||
"github.com/mattermost/platform/utils"
|
||||
)
|
||||
|
||||
type TeamEnviroment struct {
|
||||
Users []*model.User
|
||||
Channels []*model.Channel
|
||||
}
|
||||
|
||||
type AutoTeamCreator struct {
|
||||
client *model.Client
|
||||
Fuzzy bool
|
||||
NameLength utils.Range
|
||||
NameCharset string
|
||||
DomainLength utils.Range
|
||||
DomainCharset string
|
||||
EmailLength utils.Range
|
||||
EmailCharset string
|
||||
}
|
||||
|
||||
func NewAutoTeamCreator(client *model.Client) *AutoTeamCreator {
|
||||
return &AutoTeamCreator{
|
||||
client: client,
|
||||
Fuzzy: false,
|
||||
NameLength: TEAM_NAME_LEN,
|
||||
NameCharset: utils.LOWERCASE,
|
||||
DomainLength: TEAM_DOMAIN_NAME_LEN,
|
||||
DomainCharset: utils.LOWERCASE,
|
||||
EmailLength: TEAM_EMAIL_LEN,
|
||||
EmailCharset: utils.LOWERCASE,
|
||||
}
|
||||
}
|
||||
|
||||
func (cfg *AutoTeamCreator) createRandomTeam() (*model.Team, bool) {
|
||||
var teamEmail string
|
||||
var teamName string
|
||||
var teamDomain string
|
||||
if cfg.Fuzzy {
|
||||
teamEmail = utils.FuzzEmail()
|
||||
teamName = utils.FuzzName()
|
||||
teamDomain = utils.FuzzName()
|
||||
} else {
|
||||
teamEmail = utils.RandomEmail(cfg.EmailLength, cfg.EmailCharset)
|
||||
teamName = utils.RandomName(cfg.NameLength, cfg.NameCharset)
|
||||
teamDomain = utils.RandomName(cfg.NameLength, cfg.NameCharset) + model.NewId()
|
||||
}
|
||||
team := &model.Team{
|
||||
Name: teamName,
|
||||
Domain: teamDomain,
|
||||
Email: teamEmail,
|
||||
Type: model.TEAM_OPEN,
|
||||
}
|
||||
|
||||
result, err := cfg.client.CreateTeam(team)
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
createdTeam := result.Data.(*model.Team)
|
||||
return createdTeam, true
|
||||
}
|
||||
|
||||
func (cfg *AutoTeamCreator) CreateTestTeams(num utils.Range) ([]*model.Team, bool) {
|
||||
numTeams := utils.RandIntFromRange(num)
|
||||
teams := make([]*model.Team, numTeams)
|
||||
|
||||
for i := 0; i < numTeams; i++ {
|
||||
var err bool
|
||||
teams[i], err = cfg.createRandomTeam()
|
||||
if err != true {
|
||||
return teams, false
|
||||
}
|
||||
}
|
||||
|
||||
return teams, true
|
||||
}
|
||||
92
api/auto_users.go
Обычный файл
92
api/auto_users.go
Обычный файл
@@ -0,0 +1,92 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/mattermost/platform/model"
|
||||
"github.com/mattermost/platform/utils"
|
||||
)
|
||||
|
||||
type AutoUserCreator struct {
|
||||
client *model.Client
|
||||
teamID string
|
||||
EmailLength utils.Range
|
||||
EmailCharset string
|
||||
NameLength utils.Range
|
||||
NameCharset string
|
||||
Fuzzy bool
|
||||
}
|
||||
|
||||
func NewAutoUserCreator(client *model.Client, teamID string) *AutoUserCreator {
|
||||
return &AutoUserCreator{
|
||||
client: client,
|
||||
teamID: teamID,
|
||||
EmailLength: USER_EMAIL_LEN,
|
||||
EmailCharset: utils.LOWERCASE,
|
||||
NameLength: USER_NAME_LEN,
|
||||
NameCharset: utils.LOWERCASE,
|
||||
Fuzzy: false,
|
||||
}
|
||||
}
|
||||
|
||||
// Basic test team and user so you always know one
|
||||
func CreateBasicUser(client *model.Client) *model.AppError {
|
||||
result, _ := client.FindTeamByDomain(BTEST_TEAM_DOMAIN_NAME, true)
|
||||
if result.Data.(bool) == false {
|
||||
newteam := &model.Team{Name: BTEST_TEAM_NAME, Domain: BTEST_TEAM_DOMAIN_NAME, Email: BTEST_TEAM_EMAIL, Type: BTEST_TEAM_TYPE}
|
||||
result, err := client.CreateTeam(newteam)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
basicteam := result.Data.(*model.Team)
|
||||
newuser := &model.User{TeamId: basicteam.Id, Email: BTEST_USER_EMAIL, FullName: BTEST_USER_NAME, Password: BTEST_USER_PASSWORD}
|
||||
result, err = client.CreateUser(newuser, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
Srv.Store.User().VerifyEmail(result.Data.(*model.User).Id)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cfg *AutoUserCreator) createRandomUser() (*model.User, bool) {
|
||||
var userEmail string
|
||||
var userName string
|
||||
if cfg.Fuzzy {
|
||||
userEmail = utils.RandString(FUZZ_USER_EMAIL_PREFIX_LEN, utils.LOWERCASE) + "-" + utils.FuzzEmail()
|
||||
userName = utils.FuzzName()
|
||||
} else {
|
||||
userEmail = utils.RandomEmail(cfg.EmailLength, cfg.EmailCharset)
|
||||
userName = utils.RandomName(cfg.NameLength, cfg.NameCharset)
|
||||
}
|
||||
|
||||
user := &model.User{
|
||||
TeamId: cfg.teamID,
|
||||
Email: userEmail,
|
||||
FullName: userName,
|
||||
Password: USER_PASSWORD}
|
||||
|
||||
result, err := cfg.client.CreateUser(user, "")
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
// We need to cheat to verify the user's email
|
||||
Srv.Store.User().VerifyEmail(result.Data.(*model.User).Id)
|
||||
return result.Data.(*model.User), true
|
||||
}
|
||||
|
||||
func (cfg *AutoUserCreator) CreateTestUsers(num utils.Range) ([]*model.User, bool) {
|
||||
numUsers := utils.RandIntFromRange(num)
|
||||
users := make([]*model.User, numUsers)
|
||||
|
||||
for i := 0; i < numUsers; i++ {
|
||||
var err bool
|
||||
users[i], err = cfg.createRandomUser()
|
||||
if err != true {
|
||||
return users, false
|
||||
}
|
||||
}
|
||||
|
||||
return users, true
|
||||
}
|
||||
713
api/channel.go
Обычный файл
713
api/channel.go
Обычный файл
@@ -0,0 +1,713 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
l4g "code.google.com/p/log4go"
|
||||
"fmt"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/mattermost/platform/model"
|
||||
"github.com/mattermost/platform/store"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func InitChannel(r *mux.Router) {
|
||||
l4g.Debug("Initializing channel api routes")
|
||||
|
||||
sr := r.PathPrefix("/channels").Subrouter()
|
||||
sr.Handle("/", ApiUserRequiredActivity(getChannels, false)).Methods("GET")
|
||||
sr.Handle("/more", ApiUserRequired(getMoreChannels)).Methods("GET")
|
||||
sr.Handle("/create", ApiUserRequired(createChannel)).Methods("POST")
|
||||
sr.Handle("/create_direct", ApiUserRequired(createDirectChannel)).Methods("POST")
|
||||
sr.Handle("/update", ApiUserRequired(updateChannel)).Methods("POST")
|
||||
sr.Handle("/update_desc", ApiUserRequired(updateChannelDesc)).Methods("POST")
|
||||
sr.Handle("/update_notify_level", ApiUserRequired(updateNotifyLevel)).Methods("POST")
|
||||
sr.Handle("/{id:[A-Za-z0-9]+}/extra_info", ApiUserRequired(getChannelExtraInfo)).Methods("GET")
|
||||
sr.Handle("/{id:[A-Za-z0-9]+}/join", ApiUserRequired(joinChannel)).Methods("POST")
|
||||
sr.Handle("/{id:[A-Za-z0-9]+}/leave", ApiUserRequired(leaveChannel)).Methods("POST")
|
||||
sr.Handle("/{id:[A-Za-z0-9]+}/delete", ApiUserRequired(deleteChannel)).Methods("POST")
|
||||
sr.Handle("/{id:[A-Za-z0-9]+}/add", ApiUserRequired(addChannelMember)).Methods("POST")
|
||||
sr.Handle("/{id:[A-Za-z0-9]+}/remove", ApiUserRequired(removeChannelMember)).Methods("POST")
|
||||
sr.Handle("/{id:[A-Za-z0-9]+}/update_last_viewed_at", ApiUserRequired(updateLastViewedAt)).Methods("POST")
|
||||
|
||||
}
|
||||
|
||||
func createChannel(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
channel := model.ChannelFromJson(r.Body)
|
||||
|
||||
if channel == nil {
|
||||
c.SetInvalidParam("createChannel", "channel")
|
||||
return
|
||||
}
|
||||
|
||||
if !c.HasPermissionsToTeam(channel.TeamId, "createChannel") {
|
||||
return
|
||||
}
|
||||
|
||||
if channel.Type == model.CHANNEL_DIRECT {
|
||||
c.Err = model.NewAppError("createDirectChannel", "Must use createDirectChannel api service for direct message channel creation", "")
|
||||
return
|
||||
}
|
||||
|
||||
if strings.Index(channel.Name, "__") > 0 {
|
||||
c.Err = model.NewAppError("createDirectChannel", "Invalid character '__' in channel name for non-direct channel", "")
|
||||
return
|
||||
}
|
||||
|
||||
if sc, err := CreateChannel(c, channel, r.URL.Path, true); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
} else {
|
||||
w.Write([]byte(sc.ToJson()))
|
||||
}
|
||||
}
|
||||
|
||||
func CreateChannel(c *Context, channel *model.Channel, path string, addMember bool) (*model.Channel, *model.AppError) {
|
||||
if result := <-Srv.Store.Channel().Save(channel); result.Err != nil {
|
||||
return nil, result.Err
|
||||
} else {
|
||||
sc := result.Data.(*model.Channel)
|
||||
|
||||
if addMember {
|
||||
cm := &model.ChannelMember{ChannelId: sc.Id, UserId: c.Session.UserId,
|
||||
Roles: model.CHANNEL_ROLE_ADMIN, NotifyLevel: model.CHANNEL_NOTIFY_ALL}
|
||||
|
||||
if cmresult := <-Srv.Store.Channel().SaveMember(cm); cmresult.Err != nil {
|
||||
return nil, cmresult.Err
|
||||
}
|
||||
}
|
||||
|
||||
c.LogAudit("name=" + channel.Name)
|
||||
|
||||
return sc, nil
|
||||
}
|
||||
}
|
||||
|
||||
func createDirectChannel(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
data := model.MapFromJson(r.Body)
|
||||
|
||||
userId := data["user_id"]
|
||||
if len(userId) != 26 {
|
||||
c.SetInvalidParam("createDirectChannel", "user_id")
|
||||
return
|
||||
}
|
||||
|
||||
if !c.HasPermissionsToTeam(c.Session.TeamId, "createDirectChannel") {
|
||||
return
|
||||
}
|
||||
|
||||
if sc, err := CreateDirectChannel(c, userId, r.URL.Path); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
} else {
|
||||
w.Write([]byte(sc.ToJson()))
|
||||
}
|
||||
}
|
||||
|
||||
func CreateDirectChannel(c *Context, otherUserId string, path string) (*model.Channel, *model.AppError) {
|
||||
if len(otherUserId) != 26 {
|
||||
return nil, model.NewAppError("CreateDirectChannel", "Invalid other user id ", otherUserId)
|
||||
}
|
||||
|
||||
uc := Srv.Store.User().Get(otherUserId)
|
||||
|
||||
channel := new(model.Channel)
|
||||
|
||||
channel.DisplayName = ""
|
||||
if otherUserId > c.Session.UserId {
|
||||
channel.Name = c.Session.UserId + "__" + otherUserId
|
||||
} else {
|
||||
channel.Name = otherUserId + "__" + c.Session.UserId
|
||||
}
|
||||
|
||||
channel.TeamId = c.Session.TeamId
|
||||
channel.Description = ""
|
||||
channel.Type = model.CHANNEL_DIRECT
|
||||
|
||||
if uresult := <-uc; uresult.Err != nil {
|
||||
return nil, model.NewAppError("CreateDirectChannel", "Invalid other user id ", otherUserId)
|
||||
}
|
||||
|
||||
if sc, err := CreateChannel(c, channel, path, true); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
cm := &model.ChannelMember{ChannelId: sc.Id, UserId: otherUserId,
|
||||
Roles: "", NotifyLevel: model.CHANNEL_NOTIFY_ALL}
|
||||
|
||||
if cmresult := <-Srv.Store.Channel().SaveMember(cm); cmresult.Err != nil {
|
||||
return nil, cmresult.Err
|
||||
}
|
||||
|
||||
return sc, nil
|
||||
}
|
||||
}
|
||||
|
||||
func updateChannel(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
channel := model.ChannelFromJson(r.Body)
|
||||
|
||||
if channel == nil {
|
||||
c.SetInvalidParam("updateChannel", "channel")
|
||||
return
|
||||
}
|
||||
|
||||
sc := Srv.Store.Channel().Get(channel.Id)
|
||||
cmc := Srv.Store.Channel().GetMember(channel.Id, c.Session.UserId)
|
||||
|
||||
if cresult := <-sc; cresult.Err != nil {
|
||||
c.Err = cresult.Err
|
||||
return
|
||||
} else if cmcresult := <-cmc; cmcresult.Err != nil {
|
||||
c.Err = cmcresult.Err
|
||||
return
|
||||
} else {
|
||||
oldChannel := cresult.Data.(*model.Channel)
|
||||
channelMember := cmcresult.Data.(model.ChannelMember)
|
||||
if !c.HasPermissionsToTeam(oldChannel.TeamId, "updateChannel") {
|
||||
return
|
||||
}
|
||||
|
||||
if !strings.Contains(channelMember.Roles, model.CHANNEL_ROLE_ADMIN) && !strings.Contains(c.Session.Roles, model.ROLE_ADMIN) {
|
||||
c.Err = model.NewAppError("updateChannel", "You do not have the appropriate permissions", "")
|
||||
c.Err.StatusCode = http.StatusForbidden
|
||||
return
|
||||
}
|
||||
|
||||
if oldChannel.DeleteAt > 0 {
|
||||
c.Err = model.NewAppError("updateChannel", "The channel has been archived or deleted", "")
|
||||
c.Err.StatusCode = http.StatusBadRequest
|
||||
return
|
||||
}
|
||||
|
||||
if oldChannel.Name == model.DEFAULT_CHANNEL {
|
||||
c.Err = model.NewAppError("updateChannel", "Cannot update the default channel "+model.DEFAULT_CHANNEL, "")
|
||||
c.Err.StatusCode = http.StatusForbidden
|
||||
return
|
||||
}
|
||||
|
||||
oldChannel.Description = channel.Description
|
||||
|
||||
if len(channel.DisplayName) > 0 {
|
||||
oldChannel.DisplayName = channel.DisplayName
|
||||
}
|
||||
|
||||
if len(channel.Name) > 0 {
|
||||
oldChannel.Name = channel.Name
|
||||
}
|
||||
|
||||
if len(channel.Type) > 0 {
|
||||
oldChannel.Type = channel.Type
|
||||
}
|
||||
|
||||
if ucresult := <-Srv.Store.Channel().Update(oldChannel); ucresult.Err != nil {
|
||||
c.Err = ucresult.Err
|
||||
return
|
||||
} else {
|
||||
c.LogAudit("name=" + channel.Name)
|
||||
w.Write([]byte(oldChannel.ToJson()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func updateChannelDesc(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
props := model.MapFromJson(r.Body)
|
||||
channelId := props["channel_id"]
|
||||
if len(channelId) != 26 {
|
||||
c.SetInvalidParam("updateChannelDesc", "channel_id")
|
||||
return
|
||||
}
|
||||
|
||||
channelDesc := props["channel_description"]
|
||||
if len(channelDesc) > 1024 {
|
||||
c.SetInvalidParam("updateChannelDesc", "channel_description")
|
||||
return
|
||||
}
|
||||
|
||||
sc := Srv.Store.Channel().Get(channelId)
|
||||
cmc := Srv.Store.Channel().GetMember(channelId, c.Session.UserId)
|
||||
|
||||
if cresult := <-sc; cresult.Err != nil {
|
||||
c.Err = cresult.Err
|
||||
return
|
||||
} else if cmcresult := <-cmc; cmcresult.Err != nil {
|
||||
c.Err = cmcresult.Err
|
||||
return
|
||||
} else {
|
||||
channel := cresult.Data.(*model.Channel)
|
||||
// Don't need to do anything channel member, just wanted to confirm it exists
|
||||
|
||||
if !c.HasPermissionsToTeam(channel.TeamId, "updateChannelDesc") {
|
||||
return
|
||||
}
|
||||
|
||||
channel.Description = channelDesc
|
||||
|
||||
if ucresult := <-Srv.Store.Channel().Update(channel); ucresult.Err != nil {
|
||||
c.Err = ucresult.Err
|
||||
return
|
||||
} else {
|
||||
c.LogAudit("name=" + channel.Name)
|
||||
w.Write([]byte(channel.ToJson()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getChannels(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// user is already in the newtork
|
||||
|
||||
if result := <-Srv.Store.Channel().GetChannels(c.Session.TeamId, c.Session.UserId); result.Err != nil {
|
||||
if result.Err.Message == "No channels were found" {
|
||||
// lets make sure the user is valid
|
||||
if result := <-Srv.Store.User().Get(c.Session.UserId); result.Err != nil {
|
||||
c.Err = result.Err
|
||||
c.RemoveSessionCookie(w)
|
||||
l4g.Error("Error in getting users profile for id=%v forcing logout", c.Session.UserId)
|
||||
return
|
||||
}
|
||||
}
|
||||
c.Err = result.Err
|
||||
return
|
||||
} else if HandleEtag(result.Data.(*model.ChannelList).Etag(), w, r) {
|
||||
return
|
||||
} else {
|
||||
data := result.Data.(*model.ChannelList)
|
||||
w.Header().Set(model.HEADER_ETAG_SERVER, data.Etag())
|
||||
w.Write([]byte(data.ToJson()))
|
||||
}
|
||||
}
|
||||
|
||||
func getMoreChannels(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// user is already in the newtork
|
||||
|
||||
if result := <-Srv.Store.Channel().GetMoreChannels(c.Session.TeamId, c.Session.UserId); result.Err != nil {
|
||||
c.Err = result.Err
|
||||
return
|
||||
} else if HandleEtag(result.Data.(*model.ChannelList).Etag(), w, r) {
|
||||
return
|
||||
} else {
|
||||
data := result.Data.(*model.ChannelList)
|
||||
w.Header().Set(model.HEADER_ETAG_SERVER, data.Etag())
|
||||
w.Write([]byte(data.ToJson()))
|
||||
}
|
||||
}
|
||||
|
||||
func joinChannel(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
params := mux.Vars(r)
|
||||
channelId := params["id"]
|
||||
|
||||
JoinChannel(c, channelId, r.URL.Path)
|
||||
|
||||
if c.Err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
result := make(map[string]string)
|
||||
result["id"] = channelId
|
||||
w.Write([]byte(model.MapToJson(result)))
|
||||
}
|
||||
|
||||
func JoinChannel(c *Context, channelId string, path string) {
|
||||
|
||||
sc := Srv.Store.Channel().Get(channelId)
|
||||
uc := Srv.Store.User().Get(c.Session.UserId)
|
||||
|
||||
if cresult := <-sc; cresult.Err != nil {
|
||||
c.Err = cresult.Err
|
||||
return
|
||||
} else if uresult := <-uc; uresult.Err != nil {
|
||||
c.Err = uresult.Err
|
||||
return
|
||||
} else {
|
||||
channel := cresult.Data.(*model.Channel)
|
||||
user := uresult.Data.(*model.User)
|
||||
|
||||
if !c.HasPermissionsToTeam(channel.TeamId, "joinChannel") {
|
||||
return
|
||||
}
|
||||
|
||||
if channel.DeleteAt > 0 {
|
||||
c.Err = model.NewAppError("joinChannel", "The channel has been archived or deleted", "")
|
||||
c.Err.StatusCode = http.StatusBadRequest
|
||||
return
|
||||
}
|
||||
|
||||
if channel.Type == model.CHANNEL_OPEN {
|
||||
cm := &model.ChannelMember{ChannelId: channel.Id, UserId: c.Session.UserId, NotifyLevel: model.CHANNEL_NOTIFY_ALL}
|
||||
|
||||
if cmresult := <-Srv.Store.Channel().SaveMember(cm); cmresult.Err != nil {
|
||||
c.Err = cmresult.Err
|
||||
return
|
||||
}
|
||||
|
||||
post := &model.Post{ChannelId: channel.Id, Message: fmt.Sprintf(
|
||||
`User %v has joined this channel.`,
|
||||
user.Username)}
|
||||
if _, err := CreatePost(c, post, false); err != nil {
|
||||
l4g.Error("Failed to post join message %v", err)
|
||||
c.Err = model.NewAppError("joinChannel", "Failed to send join request", "")
|
||||
return
|
||||
}
|
||||
} else {
|
||||
c.Err = model.NewAppError("joinChannel", "You do not have the appropriate permissions", "")
|
||||
c.Err.StatusCode = http.StatusForbidden
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func leaveChannel(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
params := mux.Vars(r)
|
||||
id := params["id"]
|
||||
|
||||
sc := Srv.Store.Channel().Get(id)
|
||||
uc := Srv.Store.User().Get(c.Session.UserId)
|
||||
|
||||
if cresult := <-sc; cresult.Err != nil {
|
||||
c.Err = cresult.Err
|
||||
return
|
||||
} else if uresult := <-uc; uresult.Err != nil {
|
||||
c.Err = cresult.Err
|
||||
return
|
||||
} else {
|
||||
channel := cresult.Data.(*model.Channel)
|
||||
user := uresult.Data.(*model.User)
|
||||
|
||||
if !c.HasPermissionsToTeam(channel.TeamId, "leaveChannel") {
|
||||
return
|
||||
}
|
||||
|
||||
if channel.DeleteAt > 0 {
|
||||
c.Err = model.NewAppError("leaveChannel", "The channel has been archived or deleted", "")
|
||||
c.Err.StatusCode = http.StatusBadRequest
|
||||
return
|
||||
}
|
||||
|
||||
if channel.Type == model.CHANNEL_DIRECT {
|
||||
c.Err = model.NewAppError("leaveChannel", "Cannot leave a direct message channel", "")
|
||||
c.Err.StatusCode = http.StatusForbidden
|
||||
return
|
||||
}
|
||||
|
||||
if channel.Name == model.DEFAULT_CHANNEL {
|
||||
c.Err = model.NewAppError("leaveChannel", "Cannot leave the default channel "+model.DEFAULT_CHANNEL, "")
|
||||
c.Err.StatusCode = http.StatusForbidden
|
||||
return
|
||||
}
|
||||
|
||||
if cmresult := <-Srv.Store.Channel().RemoveMember(channel.Id, c.Session.UserId); cmresult.Err != nil {
|
||||
c.Err = cmresult.Err
|
||||
return
|
||||
}
|
||||
|
||||
post := &model.Post{ChannelId: channel.Id, Message: fmt.Sprintf(
|
||||
`%v has left the channel.`,
|
||||
user.Username)}
|
||||
if _, err := CreatePost(c, post, false); err != nil {
|
||||
l4g.Error("Failed to post leave message %v", err)
|
||||
c.Err = model.NewAppError("leaveChannel", "Failed to send leave message", "")
|
||||
return
|
||||
}
|
||||
|
||||
result := make(map[string]string)
|
||||
result["id"] = channel.Id
|
||||
w.Write([]byte(model.MapToJson(result)))
|
||||
}
|
||||
}
|
||||
|
||||
func deleteChannel(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
params := mux.Vars(r)
|
||||
id := params["id"]
|
||||
|
||||
sc := Srv.Store.Channel().Get(id)
|
||||
scm := Srv.Store.Channel().GetMember(id, c.Session.UserId)
|
||||
uc := Srv.Store.User().Get(c.Session.UserId)
|
||||
|
||||
if cresult := <-sc; cresult.Err != nil {
|
||||
c.Err = cresult.Err
|
||||
return
|
||||
} else if uresult := <-uc; uresult.Err != nil {
|
||||
c.Err = cresult.Err
|
||||
return
|
||||
} else if scmresult := <-scm; scmresult.Err != nil {
|
||||
c.Err = scmresult.Err
|
||||
return
|
||||
} else {
|
||||
channel := cresult.Data.(*model.Channel)
|
||||
user := uresult.Data.(*model.User)
|
||||
channelMember := scmresult.Data.(model.ChannelMember)
|
||||
|
||||
if !c.HasPermissionsToTeam(channel.TeamId, "deleteChannel") {
|
||||
return
|
||||
}
|
||||
|
||||
if !strings.Contains(channelMember.Roles, model.CHANNEL_ROLE_ADMIN) && !strings.Contains(c.Session.Roles, model.ROLE_ADMIN) {
|
||||
c.Err = model.NewAppError("deleteChannel", "You do not have the appropriate permissions", "")
|
||||
c.Err.StatusCode = http.StatusForbidden
|
||||
return
|
||||
}
|
||||
|
||||
if channel.DeleteAt > 0 {
|
||||
c.Err = model.NewAppError("deleteChannel", "The channel has been archived or deleted", "")
|
||||
c.Err.StatusCode = http.StatusBadRequest
|
||||
return
|
||||
}
|
||||
|
||||
if channel.Name == model.DEFAULT_CHANNEL {
|
||||
c.Err = model.NewAppError("deleteChannel", "Cannot delete the default channel "+model.DEFAULT_CHANNEL, "")
|
||||
c.Err.StatusCode = http.StatusForbidden
|
||||
return
|
||||
}
|
||||
|
||||
if dresult := <-Srv.Store.Channel().Delete(channel.Id, model.GetMillis()); dresult.Err != nil {
|
||||
c.Err = dresult.Err
|
||||
return
|
||||
}
|
||||
|
||||
c.LogAudit("name=" + channel.Name)
|
||||
|
||||
post := &model.Post{ChannelId: channel.Id, Message: fmt.Sprintf(
|
||||
`%v has archived the channel.`,
|
||||
user.Username)}
|
||||
if _, err := CreatePost(c, post, false); err != nil {
|
||||
l4g.Error("Failed to post archive message %v", err)
|
||||
c.Err = model.NewAppError("deleteChannel", "Failed to send archive message", "")
|
||||
return
|
||||
}
|
||||
|
||||
result := make(map[string]string)
|
||||
result["id"] = channel.Id
|
||||
w.Write([]byte(model.MapToJson(result)))
|
||||
}
|
||||
}
|
||||
|
||||
func updateLastViewedAt(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
params := mux.Vars(r)
|
||||
id := params["id"]
|
||||
|
||||
Srv.Store.Channel().UpdateLastViewedAt(id, c.Session.UserId)
|
||||
|
||||
message := model.NewMessage(c.Session.TeamId, id, c.Session.UserId, model.ACTION_VIEWED)
|
||||
message.Add("channel_id", id)
|
||||
|
||||
store.PublishAndForget(message)
|
||||
|
||||
result := make(map[string]string)
|
||||
result["id"] = id
|
||||
w.Write([]byte(model.MapToJson(result)))
|
||||
}
|
||||
|
||||
func getChannelExtraInfo(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
params := mux.Vars(r)
|
||||
id := params["id"]
|
||||
|
||||
sc := Srv.Store.Channel().Get(id)
|
||||
scm := Srv.Store.Channel().GetMember(id, c.Session.UserId)
|
||||
ecm := Srv.Store.Channel().GetExtraMembers(id, 20)
|
||||
|
||||
if cresult := <-sc; cresult.Err != nil {
|
||||
c.Err = cresult.Err
|
||||
return
|
||||
} else if cmresult := <-scm; cmresult.Err != nil {
|
||||
c.Err = cmresult.Err
|
||||
return
|
||||
} else if ecmresult := <-ecm; ecmresult.Err != nil {
|
||||
c.Err = ecmresult.Err
|
||||
return
|
||||
} else {
|
||||
channel := cresult.Data.(*model.Channel)
|
||||
member := cmresult.Data.(model.ChannelMember)
|
||||
extraMembers := ecmresult.Data.([]model.ExtraMember)
|
||||
|
||||
if !c.HasPermissionsToTeam(channel.TeamId, "getChannelExtraInfo") {
|
||||
return
|
||||
}
|
||||
|
||||
if !c.HasPermissionsToUser(member.UserId, "getChannelExtraInfo") {
|
||||
return
|
||||
}
|
||||
|
||||
if channel.DeleteAt > 0 {
|
||||
c.Err = model.NewAppError("getChannelExtraInfo", "The channel has been archived or deleted", "")
|
||||
c.Err.StatusCode = http.StatusBadRequest
|
||||
return
|
||||
}
|
||||
|
||||
data := model.ChannelExtra{Id: channel.Id, Members: extraMembers}
|
||||
w.Header().Set("Expires", "-1")
|
||||
w.Write([]byte(data.ToJson()))
|
||||
}
|
||||
}
|
||||
|
||||
func addChannelMember(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
params := mux.Vars(r)
|
||||
id := params["id"]
|
||||
|
||||
data := model.MapFromJson(r.Body)
|
||||
userId := data["user_id"]
|
||||
|
||||
if len(userId) != 26 {
|
||||
c.SetInvalidParam("addChannelMember", "user_id")
|
||||
return
|
||||
}
|
||||
|
||||
cchan := Srv.Store.Channel().CheckPermissionsTo(c.Session.TeamId, id, c.Session.UserId)
|
||||
sc := Srv.Store.Channel().Get(id)
|
||||
ouc := Srv.Store.User().Get(c.Session.UserId)
|
||||
nuc := Srv.Store.User().Get(userId)
|
||||
|
||||
// Only need to be a member of the channel to add a new member
|
||||
if !c.HasPermissionsToChannel(cchan, "addChannelMember") {
|
||||
return
|
||||
}
|
||||
|
||||
if nresult := <-nuc; nresult.Err != nil {
|
||||
c.Err = model.NewAppError("addChannelMember", "Failed to find user to be added", "")
|
||||
return
|
||||
} else if cresult := <-sc; cresult.Err != nil {
|
||||
c.Err = model.NewAppError("addChannelMember", "Failed to find channel", "")
|
||||
return
|
||||
} else {
|
||||
channel := cresult.Data.(*model.Channel)
|
||||
nUser := nresult.Data.(*model.User)
|
||||
|
||||
if channel.DeleteAt > 0 {
|
||||
c.Err = model.NewAppError("updateChannel", "The channel has been archived or deleted", "")
|
||||
c.Err.StatusCode = http.StatusBadRequest
|
||||
return
|
||||
}
|
||||
|
||||
if oresult := <-ouc; oresult.Err != nil {
|
||||
c.Err = model.NewAppError("addChannelMember", "Failed to find user doing the adding", "")
|
||||
return
|
||||
} else {
|
||||
oUser := oresult.Data.(*model.User)
|
||||
|
||||
cm := &model.ChannelMember{ChannelId: channel.Id, UserId: userId, NotifyLevel: model.CHANNEL_NOTIFY_ALL}
|
||||
|
||||
if cmresult := <-Srv.Store.Channel().SaveMember(cm); cmresult.Err != nil {
|
||||
l4g.Error("Failed to add member user_id=%v channel_id=%v err=%v", userId, id, cmresult.Err)
|
||||
c.Err = model.NewAppError("addChannelMember", "Failed to add user to channel", "")
|
||||
return
|
||||
}
|
||||
|
||||
post := &model.Post{ChannelId: id, Message: fmt.Sprintf(
|
||||
`%v added to the channel by %v`,
|
||||
nUser.Username, oUser.Username)}
|
||||
if _, err := CreatePost(c, post, false); err != nil {
|
||||
l4g.Error("Failed to post add message %v", err)
|
||||
c.Err = model.NewAppError("addChannelMember", "Failed to add member to channel", "")
|
||||
return
|
||||
}
|
||||
|
||||
c.LogAudit("name=" + channel.Name + " user_id=" + userId)
|
||||
|
||||
<-Srv.Store.Channel().UpdateLastViewedAt(id, oUser.Id)
|
||||
w.Write([]byte(cm.ToJson()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func removeChannelMember(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
params := mux.Vars(r)
|
||||
id := params["id"]
|
||||
|
||||
data := model.MapFromJson(r.Body)
|
||||
userId := data["user_id"]
|
||||
|
||||
if len(userId) != 26 {
|
||||
c.SetInvalidParam("addChannelMember", "user_id")
|
||||
return
|
||||
}
|
||||
|
||||
sc := Srv.Store.Channel().Get(id)
|
||||
cmc := Srv.Store.Channel().GetMember(id, c.Session.UserId)
|
||||
|
||||
if cresult := <-sc; cresult.Err != nil {
|
||||
c.Err = cresult.Err
|
||||
return
|
||||
} else if cmcresult := <-cmc; cmcresult.Err != nil {
|
||||
c.Err = cmcresult.Err
|
||||
return
|
||||
} else {
|
||||
channel := cresult.Data.(*model.Channel)
|
||||
channelMember := cmcresult.Data.(model.ChannelMember)
|
||||
|
||||
if !c.HasPermissionsToTeam(channel.TeamId, "removeChannelMember") {
|
||||
return
|
||||
}
|
||||
|
||||
if !strings.Contains(channelMember.Roles, model.CHANNEL_ROLE_ADMIN) && !strings.Contains(c.Session.Roles, model.ROLE_ADMIN) {
|
||||
c.Err = model.NewAppError("updateChannel", "You do not have the appropriate permissions ", "")
|
||||
c.Err.StatusCode = http.StatusForbidden
|
||||
return
|
||||
}
|
||||
|
||||
if channel.DeleteAt > 0 {
|
||||
c.Err = model.NewAppError("updateChannel", "The channel has been archived or deleted", "")
|
||||
c.Err.StatusCode = http.StatusBadRequest
|
||||
return
|
||||
}
|
||||
|
||||
if cmresult := <-Srv.Store.Channel().RemoveMember(id, userId); cmresult.Err != nil {
|
||||
c.Err = cmresult.Err
|
||||
return
|
||||
}
|
||||
|
||||
c.LogAudit("name=" + channel.Name + " user_id=" + userId)
|
||||
|
||||
result := make(map[string]string)
|
||||
result["channel_id"] = channel.Id
|
||||
result["removed_user_id"] = userId
|
||||
w.Write([]byte(model.MapToJson(result)))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func updateNotifyLevel(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
data := model.MapFromJson(r.Body)
|
||||
userId := data["user_id"]
|
||||
if len(userId) != 26 {
|
||||
c.SetInvalidParam("updateNotifyLevel", "user_id")
|
||||
return
|
||||
}
|
||||
|
||||
channelId := data["channel_id"]
|
||||
if len(channelId) != 26 {
|
||||
c.SetInvalidParam("updateNotifyLevel", "channel_id")
|
||||
return
|
||||
}
|
||||
|
||||
notifyLevel := data["notify_level"]
|
||||
if len(notifyLevel) == 0 || !model.IsChannelNotifyLevelValid(notifyLevel) {
|
||||
c.SetInvalidParam("updateNotifyLevel", "notify_level")
|
||||
return
|
||||
}
|
||||
|
||||
cchan := Srv.Store.Channel().CheckPermissionsTo(c.Session.TeamId, channelId, c.Session.UserId)
|
||||
|
||||
if !c.HasPermissionsToUser(userId, "updateNotifyLevel") {
|
||||
return
|
||||
}
|
||||
|
||||
if !c.HasPermissionsToChannel(cchan, "updateNotifyLevel") {
|
||||
return
|
||||
}
|
||||
|
||||
if result := <-Srv.Store.Channel().UpdateNotifyLevel(channelId, userId, notifyLevel); result.Err != nil {
|
||||
c.Err = result.Err
|
||||
return
|
||||
}
|
||||
|
||||
w.Write([]byte(model.MapToJson(data)))
|
||||
}
|
||||
287
api/channel_benchmark_test.go
Обычный файл
287
api/channel_benchmark_test.go
Обычный файл
@@ -0,0 +1,287 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/mattermost/platform/model"
|
||||
"github.com/mattermost/platform/utils"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const (
|
||||
NUM_CHANNELS = 140
|
||||
)
|
||||
|
||||
func BenchmarkCreateChannel(b *testing.B) {
|
||||
var (
|
||||
NUM_CHANNELS_RANGE = utils.Range{NUM_CHANNELS, NUM_CHANNELS}
|
||||
)
|
||||
team, _, _ := SetupBenchmark()
|
||||
|
||||
channelCreator := NewAutoChannelCreator(Client, team.Id)
|
||||
|
||||
// Benchmark Start
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
channelCreator.CreateTestChannels(NUM_CHANNELS_RANGE)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkCreateDirectChannel(b *testing.B) {
|
||||
var (
|
||||
NUM_CHANNELS_RANGE = utils.Range{NUM_CHANNELS, NUM_CHANNELS}
|
||||
)
|
||||
team, _, _ := SetupBenchmark()
|
||||
|
||||
userCreator := NewAutoUserCreator(Client, team.Id)
|
||||
users, err := userCreator.CreateTestUsers(NUM_CHANNELS_RANGE)
|
||||
if err == false {
|
||||
b.Fatal("Could not create users")
|
||||
}
|
||||
|
||||
data := make([]map[string]string, len(users))
|
||||
|
||||
for i := range data {
|
||||
newmap := map[string]string{
|
||||
"user_id": users[i].Id,
|
||||
}
|
||||
data[i] = newmap
|
||||
}
|
||||
|
||||
// Benchmark Start
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
for j := 0; j < NUM_CHANNELS; j++ {
|
||||
Client.CreateDirectChannel(data[j])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkUpdateChannel(b *testing.B) {
|
||||
var (
|
||||
NUM_CHANNELS_RANGE = utils.Range{NUM_CHANNELS, NUM_CHANNELS}
|
||||
CHANNEL_DESCRIPTION_LEN = 50
|
||||
)
|
||||
team, _, _ := SetupBenchmark()
|
||||
|
||||
channelCreator := NewAutoChannelCreator(Client, team.Id)
|
||||
channels, valid := channelCreator.CreateTestChannels(NUM_CHANNELS_RANGE)
|
||||
if valid == false {
|
||||
b.Fatal("Unable to create test channels")
|
||||
}
|
||||
|
||||
for i := range channels {
|
||||
channels[i].Description = utils.RandString(CHANNEL_DESCRIPTION_LEN, utils.ALPHANUMERIC)
|
||||
}
|
||||
|
||||
// Benchmark Start
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
for j := range channels {
|
||||
if _, err := Client.UpdateChannel(channels[j]); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkGetChannels(b *testing.B) {
|
||||
var (
|
||||
NUM_CHANNELS_RANGE = utils.Range{NUM_CHANNELS, NUM_CHANNELS}
|
||||
)
|
||||
team, _, _ := SetupBenchmark()
|
||||
|
||||
channelCreator := NewAutoChannelCreator(Client, team.Id)
|
||||
_, valid := channelCreator.CreateTestChannels(NUM_CHANNELS_RANGE)
|
||||
if valid == false {
|
||||
b.Fatal("Unable to create test channels")
|
||||
}
|
||||
|
||||
// Benchmark Start
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
Client.Must(Client.GetChannels(""))
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkGetMoreChannels(b *testing.B) {
|
||||
var (
|
||||
NUM_CHANNELS_RANGE = utils.Range{NUM_CHANNELS, NUM_CHANNELS}
|
||||
)
|
||||
team, _, _ := SetupBenchmark()
|
||||
|
||||
channelCreator := NewAutoChannelCreator(Client, team.Id)
|
||||
_, valid := channelCreator.CreateTestChannels(NUM_CHANNELS_RANGE)
|
||||
if valid == false {
|
||||
b.Fatal("Unable to create test channels")
|
||||
}
|
||||
|
||||
// Benchmark Start
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
Client.Must(Client.GetMoreChannels(""))
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkJoinChannel(b *testing.B) {
|
||||
var (
|
||||
NUM_CHANNELS_RANGE = utils.Range{NUM_CHANNELS, NUM_CHANNELS}
|
||||
)
|
||||
team, _, _ := SetupBenchmark()
|
||||
|
||||
channelCreator := NewAutoChannelCreator(Client, team.Id)
|
||||
channels, valid := channelCreator.CreateTestChannels(NUM_CHANNELS_RANGE)
|
||||
if valid == false {
|
||||
b.Fatal("Unable to create test channels")
|
||||
}
|
||||
|
||||
// Secondary test user to join channels created by primary test user
|
||||
user := &model.User{TeamId: team.Id, Email: model.NewId() + "random@test.com", FullName: "That Guy", Password: "pwd"}
|
||||
user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(user.Id)
|
||||
Client.LoginByEmail(team.Domain, user.Email, "pwd")
|
||||
|
||||
// Benchmark Start
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
for j := range channels {
|
||||
Client.Must(Client.JoinChannel(channels[j].Id))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkDeleteChannel(b *testing.B) {
|
||||
var (
|
||||
NUM_CHANNELS_RANGE = utils.Range{NUM_CHANNELS, NUM_CHANNELS}
|
||||
)
|
||||
team, _, _ := SetupBenchmark()
|
||||
|
||||
channelCreator := NewAutoChannelCreator(Client, team.Id)
|
||||
channels, valid := channelCreator.CreateTestChannels(NUM_CHANNELS_RANGE)
|
||||
if valid == false {
|
||||
b.Fatal("Unable to create test channels")
|
||||
}
|
||||
|
||||
// Benchmark Start
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
for j := range channels {
|
||||
Client.Must(Client.DeleteChannel(channels[j].Id))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkGetChannelExtraInfo(b *testing.B) {
|
||||
var (
|
||||
NUM_CHANNELS_RANGE = utils.Range{NUM_CHANNELS, NUM_CHANNELS}
|
||||
)
|
||||
team, _, _ := SetupBenchmark()
|
||||
|
||||
channelCreator := NewAutoChannelCreator(Client, team.Id)
|
||||
channels, valid := channelCreator.CreateTestChannels(NUM_CHANNELS_RANGE)
|
||||
if valid == false {
|
||||
b.Fatal("Unable to create test channels")
|
||||
}
|
||||
|
||||
// Benchmark Start
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
for j := range channels {
|
||||
Client.Must(Client.GetChannelExtraInfo(channels[j].Id))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkAddChannelMember(b *testing.B) {
|
||||
var (
|
||||
NUM_USERS = 100
|
||||
NUM_USERS_RANGE = utils.Range{NUM_USERS, NUM_USERS}
|
||||
)
|
||||
team, _, _ := SetupBenchmark()
|
||||
|
||||
channel := &model.Channel{DisplayName: "Test Channel", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channel = Client.Must(Client.CreateChannel(channel)).Data.(*model.Channel)
|
||||
|
||||
userCreator := NewAutoUserCreator(Client, team.Id)
|
||||
users, valid := userCreator.CreateTestUsers(NUM_USERS_RANGE)
|
||||
if valid == false {
|
||||
b.Fatal("Unable to create test users")
|
||||
}
|
||||
|
||||
// Benchmark Start
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
for j := range users {
|
||||
if _, err := Client.AddChannelMember(channel.Id, users[j].Id); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Is this benchmark failing? Raise your file ulimit! 2048 worked for me.
|
||||
func BenchmarkRemoveChannelMember(b *testing.B) {
|
||||
var (
|
||||
NUM_USERS = 140
|
||||
NUM_USERS_RANGE = utils.Range{NUM_USERS, NUM_USERS}
|
||||
)
|
||||
team, _, _ := SetupBenchmark()
|
||||
|
||||
channel := &model.Channel{DisplayName: "Test Channel", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channel = Client.Must(Client.CreateChannel(channel)).Data.(*model.Channel)
|
||||
|
||||
userCreator := NewAutoUserCreator(Client, team.Id)
|
||||
users, valid := userCreator.CreateTestUsers(NUM_USERS_RANGE)
|
||||
if valid == false {
|
||||
b.Fatal("Unable to create test users")
|
||||
}
|
||||
|
||||
for i := range users {
|
||||
if _, err := Client.AddChannelMember(channel.Id, users[i].Id); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Benchmark Start
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
for j := range users {
|
||||
if _, err := Client.RemoveChannelMember(channel.Id, users[j].Id); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkUpdateNotifyLevel(b *testing.B) {
|
||||
var (
|
||||
NUM_CHANNELS_RANGE = utils.Range{NUM_CHANNELS, NUM_CHANNELS}
|
||||
)
|
||||
team, user, _ := SetupBenchmark()
|
||||
|
||||
channelCreator := NewAutoChannelCreator(Client, team.Id)
|
||||
channels, valid := channelCreator.CreateTestChannels(NUM_CHANNELS_RANGE)
|
||||
if valid == false {
|
||||
b.Fatal("Unable to create test channels")
|
||||
}
|
||||
|
||||
data := make([]map[string]string, len(channels))
|
||||
|
||||
for i := range data {
|
||||
newmap := map[string]string{
|
||||
"channel_id": channels[i].Id,
|
||||
"user_id": user.Id,
|
||||
"notify_level": model.CHANNEL_NOTIFY_MENTION,
|
||||
}
|
||||
data[i] = newmap
|
||||
}
|
||||
|
||||
// Benchmark Start
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
for j := range channels {
|
||||
Client.Must(Client.UpdateNotifyLevel(data[j]))
|
||||
}
|
||||
}
|
||||
}
|
||||
787
api/channel_test.go
Обычный файл
787
api/channel_test.go
Обычный файл
@@ -0,0 +1,787 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/mattermost/platform/model"
|
||||
"net/http"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCreateChannel(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
|
||||
team2 := &model.Team{Name: "Name Team 2", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team2 = Client.Must(Client.CreateTeam(team2)).Data.(*model.Team)
|
||||
|
||||
user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(user.Id)
|
||||
|
||||
Client.LoginByEmail(team.Domain, user.Email, "pwd")
|
||||
|
||||
channel := model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
rchannel, err := Client.CreateChannel(&channel)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if rchannel.Data.(*model.Channel).Name != channel.Name {
|
||||
t.Fatal("full name didn't match")
|
||||
}
|
||||
|
||||
rget := Client.Must(Client.GetChannels("")).Data.(*model.ChannelList)
|
||||
if rget.Channels[0].Name != channel.Name {
|
||||
t.Fatal("full name didn't match")
|
||||
}
|
||||
|
||||
if _, err := Client.CreateChannel(rchannel.Data.(*model.Channel)); err == nil {
|
||||
t.Fatal("Cannot create an existing")
|
||||
}
|
||||
|
||||
rchannel.Data.(*model.Channel).Id = ""
|
||||
if _, err := Client.CreateChannel(rchannel.Data.(*model.Channel)); err != nil {
|
||||
if err.Message != "A channel with that name already exists" {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := Client.DoPost("/channels/create", "garbage"); err == nil {
|
||||
t.Fatal("should have been an error")
|
||||
}
|
||||
|
||||
channel = model.Channel{DisplayName: "Channel on Different Team", Name: "aaaa" + model.NewId() + "abbb", Type: model.CHANNEL_OPEN, TeamId: team2.Id}
|
||||
|
||||
if _, err := Client.CreateChannel(&channel); err.StatusCode != http.StatusForbidden {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
channel = model.Channel{DisplayName: "Test API Name", Name: model.NewId() + "__" + model.NewId(), Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
|
||||
if _, err := Client.CreateChannel(&channel); err == nil {
|
||||
t.Fatal("Should have errored out on invalid '__' character")
|
||||
}
|
||||
|
||||
channel = model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_DIRECT, TeamId: team.Id}
|
||||
|
||||
if _, err := Client.CreateChannel(&channel); err == nil {
|
||||
t.Fatal("Should have errored out on direct channel type")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateDirectChannel(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
|
||||
user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(user.Id)
|
||||
|
||||
user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(user2.Id)
|
||||
|
||||
Client.LoginByEmail(team.Domain, user.Email, "pwd")
|
||||
|
||||
data := make(map[string]string)
|
||||
data["user_id"] = user2.Id
|
||||
|
||||
rchannel, err := Client.CreateDirectChannel(data)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
channelName := ""
|
||||
if user2.Id > user.Id {
|
||||
channelName = user.Id + "__" + user2.Id
|
||||
} else {
|
||||
channelName = user2.Id + "__" + user.Id
|
||||
}
|
||||
|
||||
if rchannel.Data.(*model.Channel).Name != channelName {
|
||||
t.Fatal("channel name didn't match")
|
||||
}
|
||||
|
||||
if rchannel.Data.(*model.Channel).Type != model.CHANNEL_DIRECT {
|
||||
t.Fatal("channel type was not direct")
|
||||
}
|
||||
|
||||
if _, err := Client.CreateDirectChannel(data); err == nil {
|
||||
t.Fatal("channel already exists and should have failed")
|
||||
}
|
||||
|
||||
data["user_id"] = "junk"
|
||||
if _, err := Client.CreateDirectChannel(data); err == nil {
|
||||
t.Fatal("should have failed with bad user id")
|
||||
}
|
||||
|
||||
data["user_id"] = "12345678901234567890123456"
|
||||
if _, err := Client.CreateDirectChannel(data); err == nil {
|
||||
t.Fatal("should have failed with non-existent user")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestUpdateChannel(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
|
||||
userTeamAdmin := &model.User{TeamId: team.Id, Email: team.Email, FullName: "Corey Hulen", Password: "pwd"}
|
||||
userTeamAdmin = Client.Must(Client.CreateUser(userTeamAdmin, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(userTeamAdmin.Id)
|
||||
|
||||
userChannelAdmin := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
userChannelAdmin = Client.Must(Client.CreateUser(userChannelAdmin, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(userChannelAdmin.Id)
|
||||
|
||||
userStd := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
userStd = Client.Must(Client.CreateUser(userStd, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(userStd.Id)
|
||||
userStd.Roles = ""
|
||||
|
||||
Client.LoginByEmail(team.Domain, userChannelAdmin.Email, "pwd")
|
||||
|
||||
channel1 := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
|
||||
|
||||
Client.AddChannelMember(channel1.Id, userTeamAdmin.Id)
|
||||
|
||||
desc := "a" + model.NewId() + "a"
|
||||
upChannel1 := &model.Channel{Id: channel1.Id, Description: desc}
|
||||
upChannel1 = Client.Must(Client.UpdateChannel(upChannel1)).Data.(*model.Channel)
|
||||
|
||||
if upChannel1.Description != desc {
|
||||
t.Fatal("Channel admin failed to update desc")
|
||||
}
|
||||
|
||||
if upChannel1.DisplayName != channel1.DisplayName {
|
||||
t.Fatal("Channel admin failed to skip displayName")
|
||||
}
|
||||
|
||||
Client.LoginByEmail(team.Domain, userTeamAdmin.Email, "pwd")
|
||||
|
||||
desc = "b" + model.NewId() + "b"
|
||||
upChannel1 = &model.Channel{Id: channel1.Id, Description: desc}
|
||||
upChannel1 = Client.Must(Client.UpdateChannel(upChannel1)).Data.(*model.Channel)
|
||||
|
||||
if upChannel1.Description != desc {
|
||||
t.Fatal("Team admin failed to update desc")
|
||||
}
|
||||
|
||||
if upChannel1.DisplayName != channel1.DisplayName {
|
||||
t.Fatal("Team admin failed to skip displayName")
|
||||
}
|
||||
|
||||
rget := Client.Must(Client.GetChannels(""))
|
||||
data := rget.Data.(*model.ChannelList)
|
||||
for _, c := range data.Channels {
|
||||
if c.Name == model.DEFAULT_CHANNEL {
|
||||
c.Description = "new desc"
|
||||
if _, err := Client.UpdateChannel(c); err == nil {
|
||||
t.Fatal("should have errored on updating default channel")
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
Client.LoginByEmail(team.Domain, userStd.Email, "pwd")
|
||||
|
||||
if _, err := Client.UpdateChannel(upChannel1); err == nil {
|
||||
t.Fatal("Standard User should have failed to update")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateChannelDesc(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
|
||||
user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(user.Id)
|
||||
|
||||
Client.LoginByEmail(team.Domain, user.Email, "pwd")
|
||||
|
||||
channel1 := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
|
||||
|
||||
data := make(map[string]string)
|
||||
data["channel_id"] = channel1.Id
|
||||
data["channel_description"] = "new desc"
|
||||
|
||||
var upChannel1 *model.Channel
|
||||
if result, err := Client.UpdateChannelDesc(data); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
upChannel1 = result.Data.(*model.Channel)
|
||||
}
|
||||
|
||||
if upChannel1.Description != data["channel_description"] {
|
||||
t.Fatal("Failed to update desc")
|
||||
}
|
||||
|
||||
data["channel_id"] = "junk"
|
||||
if _, err := Client.UpdateChannelDesc(data); err == nil {
|
||||
t.Fatal("should have errored on junk channel id")
|
||||
}
|
||||
|
||||
data["channel_id"] = "12345678901234567890123456"
|
||||
if _, err := Client.UpdateChannelDesc(data); err == nil {
|
||||
t.Fatal("should have errored on non-existent channel id")
|
||||
}
|
||||
|
||||
data["channel_id"] = channel1.Id
|
||||
data["channel_description"] = ""
|
||||
for i := 0; i < 1050; i++ {
|
||||
data["channel_description"] += "a"
|
||||
}
|
||||
if _, err := Client.UpdateChannelDesc(data); err == nil {
|
||||
t.Fatal("should have errored on bad channel desc")
|
||||
}
|
||||
|
||||
user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(user2.Id)
|
||||
|
||||
Client.LoginByEmail(team.Domain, user2.Email, "pwd")
|
||||
|
||||
data["channel_id"] = channel1.Id
|
||||
data["channel_description"] = "new desc"
|
||||
if _, err := Client.UpdateChannelDesc(data); err == nil {
|
||||
t.Fatal("should have errored non-channel member trying to update desc")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetChannel(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
|
||||
user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(user.Id)
|
||||
|
||||
Client.LoginByEmail(team.Domain, user.Email, "pwd")
|
||||
|
||||
channel1 := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
|
||||
|
||||
channel2 := &model.Channel{DisplayName: "B Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channel2 = Client.Must(Client.CreateChannel(channel2)).Data.(*model.Channel)
|
||||
|
||||
rget := Client.Must(Client.GetChannels(""))
|
||||
data := rget.Data.(*model.ChannelList)
|
||||
|
||||
if data.Channels[0].DisplayName != channel1.DisplayName {
|
||||
t.Fatal("full name didn't match")
|
||||
}
|
||||
|
||||
if data.Channels[1].DisplayName != channel2.DisplayName {
|
||||
t.Fatal("full name didn't match")
|
||||
}
|
||||
|
||||
// test etag caching
|
||||
if cache_result, err := Client.GetChannels(rget.Etag); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if cache_result.Data.(*model.ChannelList) != nil {
|
||||
t.Log(cache_result.Data)
|
||||
t.Fatal("cache should be empty")
|
||||
}
|
||||
|
||||
if _, err := Client.UpdateLastViewedAt(channel2.Id); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetMoreChannel(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
|
||||
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(user1.Id)
|
||||
|
||||
Client.LoginByEmail(team.Domain, user1.Email, "pwd")
|
||||
|
||||
channel1 := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
|
||||
|
||||
channel2 := &model.Channel{DisplayName: "B Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channel2 = Client.Must(Client.CreateChannel(channel2)).Data.(*model.Channel)
|
||||
|
||||
user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(user2.Id)
|
||||
|
||||
Client.LoginByEmail(team.Domain, user2.Email, "pwd")
|
||||
|
||||
rget := Client.Must(Client.GetMoreChannels(""))
|
||||
data := rget.Data.(*model.ChannelList)
|
||||
|
||||
if data.Channels[0].DisplayName != channel1.DisplayName {
|
||||
t.Fatal("full name didn't match")
|
||||
}
|
||||
|
||||
if data.Channels[1].DisplayName != channel2.DisplayName {
|
||||
t.Fatal("full name didn't match")
|
||||
}
|
||||
|
||||
// test etag caching
|
||||
if cache_result, err := Client.GetMoreChannels(rget.Etag); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if cache_result.Data.(*model.ChannelList) != nil {
|
||||
t.Log(cache_result.Data)
|
||||
t.Fatal("cache should be empty")
|
||||
}
|
||||
}
|
||||
|
||||
func TestJoinChannel(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
|
||||
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(user1.Id)
|
||||
|
||||
Client.LoginByEmail(team.Domain, user1.Email, "pwd")
|
||||
|
||||
channel1 := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
|
||||
|
||||
channel3 := &model.Channel{DisplayName: "B Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_PRIVATE, TeamId: team.Id}
|
||||
channel3 = Client.Must(Client.CreateChannel(channel3)).Data.(*model.Channel)
|
||||
|
||||
user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(user2.Id)
|
||||
|
||||
Client.LoginByEmail(team.Domain, user2.Email, "pwd")
|
||||
|
||||
Client.Must(Client.JoinChannel(channel1.Id))
|
||||
|
||||
if _, err := Client.JoinChannel(channel3.Id); err == nil {
|
||||
t.Fatal("shouldn't be able to join secret group")
|
||||
}
|
||||
|
||||
data := make(map[string]string)
|
||||
data["user_id"] = user1.Id
|
||||
rchannel := Client.Must(Client.CreateDirectChannel(data)).Data.(*model.Channel)
|
||||
|
||||
user3 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
user3 = Client.Must(Client.CreateUser(user3, "")).Data.(*model.User)
|
||||
|
||||
Client.LoginByEmail(team.Domain, user3.Email, "pwd")
|
||||
|
||||
if _, err := Client.JoinChannel(rchannel.Id); err == nil {
|
||||
t.Fatal("shoudn't be able to join direct channel")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLeaveChannel(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
|
||||
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(user1.Id)
|
||||
|
||||
Client.LoginByEmail(team.Domain, user1.Email, "pwd")
|
||||
|
||||
channel1 := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
|
||||
|
||||
channel3 := &model.Channel{DisplayName: "B Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_PRIVATE, TeamId: team.Id}
|
||||
channel3 = Client.Must(Client.CreateChannel(channel3)).Data.(*model.Channel)
|
||||
|
||||
user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(user2.Id)
|
||||
|
||||
Client.LoginByEmail(team.Domain, user2.Email, "pwd")
|
||||
|
||||
Client.Must(Client.JoinChannel(channel1.Id))
|
||||
|
||||
// No error if you leave a channel you cannot see
|
||||
Client.Must(Client.LeaveChannel(channel3.Id))
|
||||
|
||||
data := make(map[string]string)
|
||||
data["user_id"] = user1.Id
|
||||
rchannel := Client.Must(Client.CreateDirectChannel(data)).Data.(*model.Channel)
|
||||
|
||||
if _, err := Client.LeaveChannel(rchannel.Id); err == nil {
|
||||
t.Fatal("should have errored, cannot leave direct channel")
|
||||
}
|
||||
|
||||
rget := Client.Must(Client.GetChannels(""))
|
||||
cdata := rget.Data.(*model.ChannelList)
|
||||
for _, c := range cdata.Channels {
|
||||
if c.Name == model.DEFAULT_CHANNEL {
|
||||
if _, err := Client.LeaveChannel(c.Id); err == nil {
|
||||
t.Fatal("should have errored on leaving default channel")
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteChannel(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
|
||||
userTeamAdmin := &model.User{TeamId: team.Id, Email: team.Email, FullName: "Corey Hulen", Password: "pwd"}
|
||||
userTeamAdmin = Client.Must(Client.CreateUser(userTeamAdmin, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(userTeamAdmin.Id)
|
||||
|
||||
userChannelAdmin := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
userChannelAdmin = Client.Must(Client.CreateUser(userChannelAdmin, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(userChannelAdmin.Id)
|
||||
|
||||
Client.LoginByEmail(team.Domain, userChannelAdmin.Email, "pwd")
|
||||
|
||||
channelMadeByCA := &model.Channel{DisplayName: "C Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channelMadeByCA = Client.Must(Client.CreateChannel(channelMadeByCA)).Data.(*model.Channel)
|
||||
|
||||
Client.AddChannelMember(channelMadeByCA.Id, userTeamAdmin.Id)
|
||||
|
||||
Client.LoginByEmail(team.Domain, userTeamAdmin.Email, "pwd")
|
||||
|
||||
channel1 := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
|
||||
|
||||
channel2 := &model.Channel{DisplayName: "B Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channel2 = Client.Must(Client.CreateChannel(channel2)).Data.(*model.Channel)
|
||||
|
||||
if _, err := Client.DeleteChannel(channel1.Id); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if _, err := Client.DeleteChannel(channelMadeByCA.Id); err != nil {
|
||||
t.Fatal("Team admin failed to delete Channel Admin's channel")
|
||||
}
|
||||
|
||||
post1 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"}
|
||||
if _, err := Client.CreatePost(post1); err == nil {
|
||||
t.Fatal("should have failed to post to deleted channel")
|
||||
}
|
||||
|
||||
userStd := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
userStd = Client.Must(Client.CreateUser(userStd, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(userStd.Id)
|
||||
|
||||
Client.LoginByEmail(team.Domain, userStd.Email, "pwd")
|
||||
|
||||
if _, err := Client.JoinChannel(channel1.Id); err == nil {
|
||||
t.Fatal("should have failed to join deleted channel")
|
||||
}
|
||||
|
||||
Client.Must(Client.JoinChannel(channel2.Id))
|
||||
|
||||
if _, err := Client.DeleteChannel(channel2.Id); err == nil {
|
||||
t.Fatal("should have failed to delete channel you're not an admin of")
|
||||
}
|
||||
|
||||
rget := Client.Must(Client.GetChannels(""))
|
||||
cdata := rget.Data.(*model.ChannelList)
|
||||
for _, c := range cdata.Channels {
|
||||
if c.Name == model.DEFAULT_CHANNEL {
|
||||
if _, err := Client.DeleteChannel(c.Id); err == nil {
|
||||
t.Fatal("should have errored on deleting default channel")
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetChannelExtraInfo(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
|
||||
user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(user.Id)
|
||||
|
||||
Client.LoginByEmail(team.Domain, user.Email, "pwd")
|
||||
|
||||
channel1 := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
|
||||
|
||||
rget := Client.Must(Client.GetChannelExtraInfo(channel1.Id)).Data.(*model.ChannelExtra)
|
||||
if rget.Id != channel1.Id {
|
||||
t.Fatal("couldnt't get extra info")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddChannelMember(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
|
||||
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(user1.Id)
|
||||
|
||||
Client.LoginByEmail(team.Domain, user1.Email, "pwd")
|
||||
|
||||
channel1 := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
|
||||
|
||||
user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(user2.Id)
|
||||
|
||||
if _, err := Client.AddChannelMember(channel1.Id, user2.Id); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if _, err := Client.AddChannelMember(channel1.Id, "dsgsdg"); err == nil {
|
||||
t.Fatal("Should have errored, bad user id")
|
||||
}
|
||||
|
||||
if _, err := Client.AddChannelMember(channel1.Id, "12345678901234567890123456"); err == nil {
|
||||
t.Fatal("Should have errored, bad user id")
|
||||
}
|
||||
|
||||
if _, err := Client.AddChannelMember(channel1.Id, user2.Id); err == nil {
|
||||
t.Fatal("Should have errored, user already a member")
|
||||
}
|
||||
|
||||
if _, err := Client.AddChannelMember("sgdsgsdg", user2.Id); err == nil {
|
||||
t.Fatal("Should have errored, bad channel id")
|
||||
}
|
||||
|
||||
channel2 := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channel2 = Client.Must(Client.CreateChannel(channel2)).Data.(*model.Channel)
|
||||
|
||||
Client.LoginByEmail(team.Domain, user2.Email, "pwd")
|
||||
|
||||
if _, err := Client.AddChannelMember(channel2.Id, user2.Id); err == nil {
|
||||
t.Fatal("Should have errored, user not in channel")
|
||||
}
|
||||
|
||||
Client.LoginByEmail(team.Domain, user1.Email, "pwd")
|
||||
|
||||
Client.Must(Client.DeleteChannel(channel2.Id))
|
||||
|
||||
if _, err := Client.AddChannelMember(channel2.Id, user2.Id); err == nil {
|
||||
t.Fatal("Should have errored, channel deleted")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestRemoveChannelMember(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
|
||||
userTeamAdmin := &model.User{TeamId: team.Id, Email: team.Email, FullName: "Corey Hulen", Password: "pwd"}
|
||||
userTeamAdmin = Client.Must(Client.CreateUser(userTeamAdmin, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(userTeamAdmin.Id)
|
||||
|
||||
userChannelAdmin := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
userChannelAdmin = Client.Must(Client.CreateUser(userChannelAdmin, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(userChannelAdmin.Id)
|
||||
|
||||
Client.LoginByEmail(team.Domain, userChannelAdmin.Email, "pwd")
|
||||
|
||||
channelMadeByCA := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channelMadeByCA = Client.Must(Client.CreateChannel(channelMadeByCA)).Data.(*model.Channel)
|
||||
|
||||
Client.Must(Client.AddChannelMember(channelMadeByCA.Id, userTeamAdmin.Id))
|
||||
|
||||
Client.LoginByEmail(team.Domain, userTeamAdmin.Email, "pwd")
|
||||
|
||||
channel1 := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
|
||||
|
||||
userStd := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
userStd = Client.Must(Client.CreateUser(userStd, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(userStd.Id)
|
||||
|
||||
Client.Must(Client.AddChannelMember(channel1.Id, userStd.Id))
|
||||
|
||||
Client.Must(Client.AddChannelMember(channelMadeByCA.Id, userStd.Id))
|
||||
|
||||
if _, err := Client.RemoveChannelMember(channel1.Id, "dsgsdg"); err == nil {
|
||||
t.Fatal("Should have errored, bad user id")
|
||||
}
|
||||
|
||||
if _, err := Client.RemoveChannelMember("sgdsgsdg", userStd.Id); err == nil {
|
||||
t.Fatal("Should have errored, bad channel id")
|
||||
}
|
||||
|
||||
if _, err := Client.RemoveChannelMember(channel1.Id, userStd.Id); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if _, err := Client.RemoveChannelMember(channelMadeByCA.Id, userStd.Id); err != nil {
|
||||
t.Fatal("Team Admin failed to remove member from Channel Admin's channel")
|
||||
}
|
||||
|
||||
channel2 := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channel2 = Client.Must(Client.CreateChannel(channel2)).Data.(*model.Channel)
|
||||
|
||||
Client.LoginByEmail(team.Domain, userStd.Email, "pwd")
|
||||
|
||||
if _, err := Client.RemoveChannelMember(channel2.Id, userStd.Id); err == nil {
|
||||
t.Fatal("Should have errored, user not channel admin")
|
||||
}
|
||||
|
||||
Client.LoginByEmail(team.Domain, userTeamAdmin.Email, "pwd")
|
||||
Client.Must(Client.AddChannelMember(channel2.Id, userStd.Id))
|
||||
|
||||
Client.Must(Client.DeleteChannel(channel2.Id))
|
||||
|
||||
if _, err := Client.RemoveChannelMember(channel2.Id, userStd.Id); err == nil {
|
||||
t.Fatal("Should have errored, channel deleted")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestUpdateNotifyLevel(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
|
||||
user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(user.Id)
|
||||
|
||||
Client.LoginByEmail(team.Domain, user.Email, "pwd")
|
||||
|
||||
channel1 := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
|
||||
|
||||
data := make(map[string]string)
|
||||
data["channel_id"] = channel1.Id
|
||||
data["user_id"] = user.Id
|
||||
data["notify_level"] = model.CHANNEL_NOTIFY_MENTION
|
||||
|
||||
if _, err := Client.UpdateNotifyLevel(data); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
rget := Client.Must(Client.GetChannels(""))
|
||||
rdata := rget.Data.(*model.ChannelList)
|
||||
if len(rdata.Members) == 0 || rdata.Members[channel1.Id].NotifyLevel != data["notify_level"] {
|
||||
t.Fatal("NotifyLevel did not update properly")
|
||||
}
|
||||
|
||||
data["user_id"] = "junk"
|
||||
if _, err := Client.UpdateNotifyLevel(data); err == nil {
|
||||
t.Fatal("Should have errored - bad user id")
|
||||
}
|
||||
|
||||
data["user_id"] = "12345678901234567890123456"
|
||||
if _, err := Client.UpdateNotifyLevel(data); err == nil {
|
||||
t.Fatal("Should have errored - bad user id")
|
||||
}
|
||||
|
||||
data["user_id"] = user.Id
|
||||
data["channel_id"] = "junk"
|
||||
if _, err := Client.UpdateNotifyLevel(data); err == nil {
|
||||
t.Fatal("Should have errored - bad channel id")
|
||||
}
|
||||
|
||||
data["channel_id"] = "12345678901234567890123456"
|
||||
if _, err := Client.UpdateNotifyLevel(data); err == nil {
|
||||
t.Fatal("Should have errored - bad channel id")
|
||||
}
|
||||
|
||||
data["channel_id"] = channel1.Id
|
||||
data["notify_level"] = ""
|
||||
if _, err := Client.UpdateNotifyLevel(data); err == nil {
|
||||
t.Fatal("Should have errored - empty notify level")
|
||||
}
|
||||
|
||||
data["notify_level"] = "junk"
|
||||
if _, err := Client.UpdateNotifyLevel(data); err == nil {
|
||||
t.Fatal("Should have errored - bad notify level")
|
||||
}
|
||||
|
||||
user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User)
|
||||
|
||||
Client.LoginByEmail(team.Domain, user2.Email, "pwd")
|
||||
|
||||
data["channel_id"] = channel1.Id
|
||||
data["user_id"] = user2.Id
|
||||
data["notify_level"] = model.CHANNEL_NOTIFY_MENTION
|
||||
if _, err := Client.UpdateNotifyLevel(data); err == nil {
|
||||
t.Fatal("Should have errored - user not in channel")
|
||||
}
|
||||
}
|
||||
|
||||
func TestFuzzyChannel(t *testing.T) {
|
||||
Setup();
|
||||
|
||||
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
|
||||
user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(user.Id)
|
||||
|
||||
Client.LoginByEmail(team.Domain, user.Email, "pwd")
|
||||
|
||||
// Strings that should pass as acceptable channel names
|
||||
var fuzzyStringsPass = []string {
|
||||
"*", "?", ".", "}{][)(><", "{}[]()<>",
|
||||
|
||||
"qahwah ( قهوة)",
|
||||
"שָׁלוֹם עֲלֵיכֶם",
|
||||
"Ramen チャーシュー chāshū",
|
||||
"言而无信",
|
||||
"Ṫ͌ó̍ ̍͂̓̍̍̀i̊ͯ͒",
|
||||
"& < &qu",
|
||||
|
||||
"' or '1'='1' -- ",
|
||||
"' or '1'='1' ({ ",
|
||||
"' or '1'='1' /* ",
|
||||
"1;DROP TABLE users",
|
||||
|
||||
"<b><i><u><strong><em>",
|
||||
|
||||
"sue@thatmightbe",
|
||||
"sue@thatmightbe.",
|
||||
"sue@thatmightbe.c",
|
||||
"sue@thatmightbe.co",
|
||||
"su+san@thatmightbe.com",
|
||||
"a@b.中国",
|
||||
"1@2.am",
|
||||
"a@b.co.uk",
|
||||
"a@b.cancerresearch",
|
||||
"local@[127.0.0.1]",
|
||||
}
|
||||
|
||||
for i := 0; i < len(fuzzyStringsPass); i++ {
|
||||
channel := model.Channel{DisplayName: fuzzyStringsPass[i], Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
|
||||
_, err := Client.CreateChannel(&channel)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
470
api/command.go
Обычный файл
470
api/command.go
Обычный файл
@@ -0,0 +1,470 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
l4g "code.google.com/p/log4go"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/mattermost/platform/model"
|
||||
"github.com/mattermost/platform/utils"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type commandHandler func(c *Context, command *model.Command) bool
|
||||
|
||||
var commands = []commandHandler{
|
||||
logoutCommand,
|
||||
joinCommand,
|
||||
loadTestCommand,
|
||||
echoCommand,
|
||||
}
|
||||
|
||||
func InitCommand(r *mux.Router) {
|
||||
l4g.Debug("Initializing command api routes")
|
||||
r.Handle("/command", ApiUserRequired(command)).Methods("POST")
|
||||
hub.Start()
|
||||
}
|
||||
|
||||
func command(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
props := model.MapFromJson(r.Body)
|
||||
|
||||
command := &model.Command{
|
||||
Command: strings.TrimSpace(props["command"]),
|
||||
ChannelId: strings.TrimSpace(props["channelId"]),
|
||||
Suggest: props["suggest"] == "true",
|
||||
Suggestions: make([]*model.SuggestCommand, 0, 128),
|
||||
}
|
||||
|
||||
checkCommand(c, command)
|
||||
|
||||
if c.Err != nil {
|
||||
return
|
||||
} else {
|
||||
w.Write([]byte(command.ToJson()))
|
||||
}
|
||||
}
|
||||
|
||||
func checkCommand(c *Context, command *model.Command) bool {
|
||||
|
||||
if len(command.Command) == 0 || strings.Index(command.Command, "/") != 0 {
|
||||
c.Err = model.NewAppError("checkCommand", "Command must start with /", "")
|
||||
return false
|
||||
}
|
||||
|
||||
if len(command.ChannelId) > 0 {
|
||||
cchan := Srv.Store.Channel().CheckPermissionsTo(c.Session.TeamId, command.ChannelId, c.Session.UserId)
|
||||
|
||||
if !c.HasPermissionsToChannel(cchan, "checkCommand") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
for _, v := range commands {
|
||||
if v(c, command) {
|
||||
return true
|
||||
} else if c.Err != nil {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func logoutCommand(c *Context, command *model.Command) bool {
|
||||
|
||||
cmd := "/logout"
|
||||
|
||||
if strings.Index(command.Command, cmd) == 0 {
|
||||
command.AddSuggestion(&model.SuggestCommand{Suggestion: cmd, Description: "Logout"})
|
||||
|
||||
if !command.Suggest {
|
||||
command.GotoLocation = "/logout"
|
||||
command.Response = model.RESP_EXECUTED
|
||||
return true
|
||||
}
|
||||
|
||||
} else if strings.Index(cmd, command.Command) == 0 {
|
||||
command.AddSuggestion(&model.SuggestCommand{Suggestion: cmd, Description: "Logout"})
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func echoCommand(c *Context, command *model.Command) bool {
|
||||
|
||||
cmd := "/echo"
|
||||
|
||||
if strings.Index(command.Command, cmd) == 0 {
|
||||
parts := strings.SplitN(command.Command, " ", 3)
|
||||
|
||||
channelName := ""
|
||||
if len(parts) >= 2 {
|
||||
channelName = parts[1]
|
||||
}
|
||||
|
||||
message := ""
|
||||
if len(parts) >= 3 {
|
||||
message = parts[2]
|
||||
}
|
||||
|
||||
if result := <-Srv.Store.Channel().GetChannels(c.Session.TeamId, c.Session.UserId); result.Err != nil {
|
||||
c.Err = result.Err
|
||||
return false
|
||||
} else {
|
||||
channels := result.Data.(*model.ChannelList)
|
||||
|
||||
for _, v := range channels.Channels {
|
||||
if v.Type == model.CHANNEL_DIRECT {
|
||||
continue
|
||||
}
|
||||
|
||||
if v.Name == channelName && !command.Suggest {
|
||||
post := &model.Post{}
|
||||
post.ChannelId = v.Id
|
||||
post.Message = message
|
||||
|
||||
if _, err := CreateValetPost(c, post); err != nil {
|
||||
c.Err = err
|
||||
return false
|
||||
}
|
||||
|
||||
command.Response = model.RESP_EXECUTED
|
||||
return true
|
||||
}
|
||||
|
||||
if len(channelName) == 0 || (strings.Index(v.Name, channelName) == 0 && len(parts) < 3) {
|
||||
command.AddSuggestion(&model.SuggestCommand{Suggestion: cmd + " " + v.Name, Description: "Echo a message using Valet in a channel"})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else if strings.Index(cmd, command.Command) == 0 {
|
||||
command.AddSuggestion(&model.SuggestCommand{Suggestion: cmd, Description: "Echo a message using Valet in a channel"})
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func joinCommand(c *Context, command *model.Command) bool {
|
||||
|
||||
// looks for "/join channel-name"
|
||||
cmd := "/join"
|
||||
|
||||
if strings.Index(command.Command, cmd) == 0 {
|
||||
|
||||
parts := strings.Split(command.Command, " ")
|
||||
|
||||
startsWith := ""
|
||||
|
||||
if len(parts) == 2 {
|
||||
startsWith = parts[1]
|
||||
}
|
||||
|
||||
if result := <-Srv.Store.Channel().GetMoreChannels(c.Session.TeamId, c.Session.UserId); result.Err != nil {
|
||||
c.Err = result.Err
|
||||
return false
|
||||
} else {
|
||||
channels := result.Data.(*model.ChannelList)
|
||||
|
||||
for _, v := range channels.Channels {
|
||||
|
||||
if v.Name == startsWith && !command.Suggest {
|
||||
|
||||
if v.Type == model.CHANNEL_DIRECT {
|
||||
return false
|
||||
}
|
||||
|
||||
JoinChannel(c, v.Id, "/command")
|
||||
|
||||
if c.Err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
command.GotoLocation = "/channels/" + v.Name
|
||||
command.Response = model.RESP_EXECUTED
|
||||
return true
|
||||
}
|
||||
|
||||
if len(startsWith) == 0 || strings.Index(v.Name, startsWith) == 0 {
|
||||
command.AddSuggestion(&model.SuggestCommand{Suggestion: cmd + " " + v.Name, Description: "Join the open channel"})
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if strings.Index(cmd, command.Command) == 0 {
|
||||
command.AddSuggestion(&model.SuggestCommand{Suggestion: cmd, Description: "Join an open channel"})
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func loadTestCommand(c *Context, command *model.Command) bool {
|
||||
cmd := "/loadtest"
|
||||
|
||||
// This command is only available when AllowTesting is true
|
||||
if !utils.Cfg.ServiceSettings.AllowTesting {
|
||||
return false
|
||||
}
|
||||
|
||||
if strings.Index(command.Command, cmd) == 0 {
|
||||
if loadTestSetupCommand(c, command) {
|
||||
return true
|
||||
}
|
||||
if loadTestUsersCommand(c, command) {
|
||||
return true
|
||||
}
|
||||
if loadTestChannelsCommand(c, command) {
|
||||
return true
|
||||
}
|
||||
if loadTestPostsCommand(c, command) {
|
||||
return true
|
||||
}
|
||||
} else if strings.Index(cmd, command.Command) == 0 {
|
||||
command.AddSuggestion(&model.SuggestCommand{Suggestion: cmd, Description: "Debug Load Testing"})
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func parseRange(command string, cmd string) (utils.Range, bool) {
|
||||
tokens := strings.Fields(strings.TrimPrefix(command, cmd))
|
||||
var begin int
|
||||
var end int
|
||||
var err1 error
|
||||
var err2 error
|
||||
switch {
|
||||
case len(tokens) == 1:
|
||||
begin, err1 = strconv.Atoi(tokens[0])
|
||||
end = begin
|
||||
if err1 != nil {
|
||||
return utils.Range{0, 0}, false
|
||||
}
|
||||
case len(tokens) >= 2:
|
||||
begin, err1 = strconv.Atoi(tokens[0])
|
||||
end, err2 = strconv.Atoi(tokens[1])
|
||||
if err1 != nil || err2 != nil {
|
||||
return utils.Range{0, 0}, false
|
||||
}
|
||||
default:
|
||||
return utils.Range{0, 0}, false
|
||||
}
|
||||
return utils.Range{begin, end}, true
|
||||
}
|
||||
|
||||
func contains(items []string, token string) bool {
|
||||
for _, elem := range items {
|
||||
if elem == token {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func loadTestSetupCommand(c *Context, command *model.Command) bool {
|
||||
cmd := "/loadtest setup"
|
||||
|
||||
if strings.Index(command.Command, cmd) == 0 && !command.Suggest {
|
||||
tokens := strings.Fields(strings.TrimPrefix(command.Command, cmd))
|
||||
doTeams := contains(tokens, "teams")
|
||||
doFuzz := contains(tokens, "fuzz")
|
||||
|
||||
numArgs := 0
|
||||
if doTeams {
|
||||
numArgs++
|
||||
}
|
||||
if doFuzz {
|
||||
numArgs++
|
||||
}
|
||||
|
||||
var numTeams int
|
||||
var numChannels int
|
||||
var numUsers int
|
||||
var numPosts int
|
||||
|
||||
// Defaults
|
||||
numTeams = 10
|
||||
numChannels = 10
|
||||
numUsers = 10
|
||||
numPosts = 10
|
||||
|
||||
if doTeams {
|
||||
if (len(tokens) - numArgs) >= 4 {
|
||||
numTeams, _ = strconv.Atoi(tokens[numArgs+0])
|
||||
numChannels, _ = strconv.Atoi(tokens[numArgs+1])
|
||||
numUsers, _ = strconv.Atoi(tokens[numArgs+2])
|
||||
numPosts, _ = strconv.Atoi(tokens[numArgs+3])
|
||||
}
|
||||
} else {
|
||||
if (len(tokens) - numArgs) >= 3 {
|
||||
numChannels, _ = strconv.Atoi(tokens[numArgs+0])
|
||||
numUsers, _ = strconv.Atoi(tokens[numArgs+1])
|
||||
numPosts, _ = strconv.Atoi(tokens[numArgs+2])
|
||||
}
|
||||
}
|
||||
client := model.NewClient(c.TeamUrl + "/api/v1")
|
||||
|
||||
if doTeams {
|
||||
if err := CreateBasicUser(client); err != nil {
|
||||
l4g.Error("Failed to create testing enviroment")
|
||||
return true
|
||||
}
|
||||
client.LoginByEmail(BTEST_TEAM_DOMAIN_NAME, BTEST_USER_EMAIL, BTEST_USER_PASSWORD)
|
||||
enviroment, err := CreateTestEnviromentWithTeams(
|
||||
client,
|
||||
utils.Range{numTeams, numTeams},
|
||||
utils.Range{numChannels, numChannels},
|
||||
utils.Range{numUsers, numUsers},
|
||||
utils.Range{numPosts, numPosts},
|
||||
doFuzz)
|
||||
if err != true {
|
||||
l4g.Error("Failed to create testing enviroment")
|
||||
return true
|
||||
} else {
|
||||
l4g.Info("Testing enviroment created")
|
||||
for i := 0; i < len(enviroment.Teams); i++ {
|
||||
l4g.Info("Team Created: " + enviroment.Teams[i].Domain)
|
||||
l4g.Info("\t User to login: " + enviroment.Enviroments[i].Users[0].Email + ", " + USER_PASSWORD)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
client.MockSession(c.Session.Id)
|
||||
CreateTestEnviromentInTeam(
|
||||
client,
|
||||
c.Session.TeamId,
|
||||
utils.Range{numChannels, numChannels},
|
||||
utils.Range{numUsers, numUsers},
|
||||
utils.Range{numPosts, numPosts},
|
||||
doFuzz)
|
||||
}
|
||||
return true
|
||||
} else if strings.Index(cmd, command.Command) == 0 {
|
||||
command.AddSuggestion(&model.SuggestCommand{
|
||||
Suggestion: cmd,
|
||||
Description: "Creates a testing enviroment in current team. [teams] [fuzz] <Num Channels> <Num Users> <NumPosts>"})
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func loadTestUsersCommand(c *Context, command *model.Command) bool {
|
||||
cmd1 := "/loadtest users"
|
||||
cmd2 := "/loadtest users fuzz"
|
||||
|
||||
if strings.Index(command.Command, cmd1) == 0 && !command.Suggest {
|
||||
cmd := cmd1
|
||||
doFuzz := false
|
||||
if strings.Index(command.Command, cmd2) == 0 {
|
||||
doFuzz = true
|
||||
cmd = cmd2
|
||||
}
|
||||
usersr, err := parseRange(command.Command, cmd)
|
||||
if err == false {
|
||||
usersr = utils.Range{10, 15}
|
||||
}
|
||||
client := model.NewClient(c.TeamUrl + "/api/v1")
|
||||
userCreator := NewAutoUserCreator(client, c.Session.TeamId)
|
||||
userCreator.Fuzzy = doFuzz
|
||||
userCreator.CreateTestUsers(usersr)
|
||||
return true
|
||||
} else if strings.Index(cmd1, command.Command) == 0 {
|
||||
command.AddSuggestion(&model.SuggestCommand{Suggestion: cmd1, Description: "Add a specified number of random users to current team <Min Users> <Max Users>"})
|
||||
command.AddSuggestion(&model.SuggestCommand{Suggestion: cmd2, Description: "Add a specified number of random users with fuzz text to current team <Min Users> <Max Users>"})
|
||||
} else if strings.Index(cmd2, command.Command) == 0 {
|
||||
command.AddSuggestion(&model.SuggestCommand{Suggestion: cmd2, Description: "Add a specified number of random users with fuzz text to current team <Min Users> <Max Users>"})
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func loadTestChannelsCommand(c *Context, command *model.Command) bool {
|
||||
cmd1 := "/loadtest channels"
|
||||
cmd2 := "/loadtest channels fuzz"
|
||||
|
||||
if strings.Index(command.Command, cmd1) == 0 && !command.Suggest {
|
||||
cmd := cmd1
|
||||
doFuzz := false
|
||||
if strings.Index(command.Command, cmd2) == 0 {
|
||||
doFuzz = true
|
||||
cmd = cmd2
|
||||
}
|
||||
channelsr, err := parseRange(command.Command, cmd)
|
||||
if err == false {
|
||||
channelsr = utils.Range{20, 30}
|
||||
}
|
||||
client := model.NewClient(c.TeamUrl + "/api/v1")
|
||||
client.MockSession(c.Session.Id)
|
||||
channelCreator := NewAutoChannelCreator(client, c.Session.TeamId)
|
||||
channelCreator.Fuzzy = doFuzz
|
||||
channelCreator.CreateTestChannels(channelsr)
|
||||
return true
|
||||
} else if strings.Index(cmd1, command.Command) == 0 {
|
||||
command.AddSuggestion(&model.SuggestCommand{Suggestion: cmd1, Description: "Add a specified number of random channels to current team <MinChannels> <MaxChannels>"})
|
||||
command.AddSuggestion(&model.SuggestCommand{Suggestion: cmd2, Description: "Add a specified number of random channels with fuzz text to current team <Min Channels> <Max Channels>"})
|
||||
} else if strings.Index(cmd2, command.Command) == 0 {
|
||||
command.AddSuggestion(&model.SuggestCommand{Suggestion: cmd2, Description: "Add a specified number of random channels with fuzz text to current team <Min Channels> <Max Channels>"})
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func loadTestPostsCommand(c *Context, command *model.Command) bool {
|
||||
cmd1 := "/loadtest posts"
|
||||
cmd2 := "/loadtest posts fuzz"
|
||||
|
||||
if strings.Index(command.Command, cmd1) == 0 && !command.Suggest {
|
||||
cmd := cmd1
|
||||
doFuzz := false
|
||||
if strings.Index(command.Command, cmd2) == 0 {
|
||||
cmd = cmd2
|
||||
doFuzz = true
|
||||
}
|
||||
|
||||
postsr, err := parseRange(command.Command, cmd)
|
||||
if err == false {
|
||||
postsr = utils.Range{20, 30}
|
||||
}
|
||||
|
||||
tokens := strings.Fields(strings.TrimPrefix(command.Command, cmd))
|
||||
rimages := utils.Range{0, 0}
|
||||
if len(tokens) >= 3 {
|
||||
if numImages, err := strconv.Atoi(tokens[2]); err == nil {
|
||||
rimages = utils.Range{numImages, numImages}
|
||||
}
|
||||
}
|
||||
|
||||
var usernames []string
|
||||
if result := <-Srv.Store.User().GetProfiles(c.Session.TeamId); result.Err == nil {
|
||||
profileUsers := result.Data.(map[string]*model.User)
|
||||
usernames = make([]string, len(profileUsers))
|
||||
i := 0
|
||||
for _, userprof := range profileUsers {
|
||||
usernames[i] = userprof.Username
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
client := model.NewClient(c.TeamUrl + "/api/v1")
|
||||
client.MockSession(c.Session.Id)
|
||||
testPoster := NewAutoPostCreator(client, command.ChannelId)
|
||||
testPoster.Fuzzy = doFuzz
|
||||
testPoster.Users = usernames
|
||||
|
||||
numImages := utils.RandIntFromRange(rimages)
|
||||
numPosts := utils.RandIntFromRange(postsr)
|
||||
for i := 0; i < numPosts; i++ {
|
||||
testPoster.HasImage = (i < numImages)
|
||||
testPoster.CreateRandomPost()
|
||||
}
|
||||
return true
|
||||
} else if strings.Index(cmd1, command.Command) == 0 {
|
||||
command.AddSuggestion(&model.SuggestCommand{Suggestion: cmd1, Description: "Add some random posts to current channel <Min Posts> <Max Posts> <Min Images> <Max Images>"})
|
||||
command.AddSuggestion(&model.SuggestCommand{Suggestion: cmd2, Description: "Add some random posts with fuzz text to current channel <Min Posts> <Max Posts> <Min Images> <Max Images>"})
|
||||
} else if strings.Index(cmd2, command.Command) == 0 {
|
||||
command.AddSuggestion(&model.SuggestCommand{Suggestion: cmd2, Description: "Add some random posts with fuzz text to current channel <Min Posts> <Max Posts> <Min Images> <Max Images>"})
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
146
api/command_test.go
Обычный файл
146
api/command_test.go
Обычный файл
@@ -0,0 +1,146 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/mattermost/platform/model"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSuggestRootCommands(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
|
||||
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(user1.Id)
|
||||
|
||||
Client.LoginByEmail(team.Domain, user1.Email, "pwd")
|
||||
|
||||
if _, err := Client.Command("", "", true); err == nil {
|
||||
t.Fatal("Should fail")
|
||||
}
|
||||
|
||||
rs1 := Client.Must(Client.Command("", "/", true)).Data.(*model.Command)
|
||||
|
||||
hasLogout := false
|
||||
for _, v := range rs1.Suggestions {
|
||||
if v.Suggestion == "/logout" {
|
||||
hasLogout = true
|
||||
}
|
||||
}
|
||||
|
||||
if !hasLogout {
|
||||
t.Log(rs1.Suggestions)
|
||||
t.Fatal("should have logout cmd")
|
||||
}
|
||||
|
||||
rs2 := Client.Must(Client.Command("", "/log", true)).Data.(*model.Command)
|
||||
|
||||
if rs2.Suggestions[0].Suggestion != "/logout" {
|
||||
t.Fatal("should have logout cmd")
|
||||
}
|
||||
|
||||
rs3 := Client.Must(Client.Command("", "/joi", true)).Data.(*model.Command)
|
||||
|
||||
if rs3.Suggestions[0].Suggestion != "/join" {
|
||||
t.Fatal("should have join cmd")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLogoutCommands(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
|
||||
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(user1.Id)
|
||||
|
||||
Client.LoginByEmail(team.Domain, user1.Email, "pwd")
|
||||
|
||||
rs1 := Client.Must(Client.Command("", "/logout", false)).Data.(*model.Command)
|
||||
if rs1.GotoLocation != "/logout" {
|
||||
t.Fatal("failed to logout")
|
||||
}
|
||||
}
|
||||
|
||||
func TestJoinCommands(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
|
||||
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(user1.Id)
|
||||
|
||||
Client.LoginByEmail(team.Domain, user1.Email, "pwd")
|
||||
|
||||
channel1 := &model.Channel{DisplayName: "AA", Name: "aa" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
|
||||
Client.Must(Client.LeaveChannel(channel1.Id))
|
||||
|
||||
channel2 := &model.Channel{DisplayName: "BB", Name: "bb" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channel2 = Client.Must(Client.CreateChannel(channel2)).Data.(*model.Channel)
|
||||
Client.Must(Client.LeaveChannel(channel2.Id))
|
||||
|
||||
user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(user1.Id)
|
||||
|
||||
data := make(map[string]string)
|
||||
data["user_id"] = user2.Id
|
||||
channel3 := Client.Must(Client.CreateDirectChannel(data)).Data.(*model.Channel)
|
||||
|
||||
rs1 := Client.Must(Client.Command("", "/join aa", true)).Data.(*model.Command)
|
||||
if rs1.Suggestions[0].Suggestion != "/join "+channel1.Name {
|
||||
t.Fatal("should have join cmd")
|
||||
}
|
||||
|
||||
rs2 := Client.Must(Client.Command("", "/join bb", true)).Data.(*model.Command)
|
||||
if rs2.Suggestions[0].Suggestion != "/join "+channel2.Name {
|
||||
t.Fatal("should have join cmd")
|
||||
}
|
||||
|
||||
rs3 := Client.Must(Client.Command("", "/join", true)).Data.(*model.Command)
|
||||
if len(rs3.Suggestions) != 2 {
|
||||
t.Fatal("should have 2 join cmd")
|
||||
}
|
||||
|
||||
rs4 := Client.Must(Client.Command("", "/join ", true)).Data.(*model.Command)
|
||||
if len(rs4.Suggestions) != 2 {
|
||||
t.Fatal("should have 2 join cmd")
|
||||
}
|
||||
|
||||
rs5 := Client.Must(Client.Command("", "/join "+channel2.Name, false)).Data.(*model.Command)
|
||||
if rs5.GotoLocation != "/channels/"+channel2.Name {
|
||||
t.Fatal("failed to join channel")
|
||||
}
|
||||
|
||||
rs6 := Client.Must(Client.Command("", "/join "+channel3.Name, false)).Data.(*model.Command)
|
||||
if rs6.GotoLocation == "/channels/"+channel3.Name {
|
||||
t.Fatal("should not have joined direct message channel")
|
||||
}
|
||||
|
||||
c1 := Client.Must(Client.GetChannels("")).Data.(*model.ChannelList)
|
||||
|
||||
if len(c1.Channels) != 3 { // 3 because of town-square and direct
|
||||
t.Fatal("didn't join channel")
|
||||
}
|
||||
|
||||
found := false
|
||||
for _, c := range c1.Channels {
|
||||
if c.Name == channel2.Name {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Fatal("didn't join channel")
|
||||
}
|
||||
}
|
||||
375
api/context.go
Обычный файл
375
api/context.go
Обычный файл
@@ -0,0 +1,375 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
l4g "code.google.com/p/log4go"
|
||||
"github.com/mattermost/platform/model"
|
||||
"github.com/mattermost/platform/store"
|
||||
"github.com/mattermost/platform/utils"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var sessionCache *utils.Cache = utils.NewLru(model.SESSION_CACHE_SIZE)
|
||||
|
||||
type Context struct {
|
||||
Session model.Session
|
||||
RequestId string
|
||||
IpAddress string
|
||||
TeamUrl string
|
||||
Path string
|
||||
Err *model.AppError
|
||||
}
|
||||
|
||||
type Page struct {
|
||||
TemplateName string
|
||||
Title string
|
||||
SiteName string
|
||||
FeedbackEmail string
|
||||
TeamUrl string
|
||||
Props map[string]string
|
||||
}
|
||||
|
||||
func ApiAppHandler(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler {
|
||||
return &handler{h, false, false, true, false}
|
||||
}
|
||||
|
||||
func AppHandler(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler {
|
||||
return &handler{h, false, false, false, false}
|
||||
}
|
||||
|
||||
func ApiUserRequired(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler {
|
||||
return &handler{h, true, false, true, true}
|
||||
}
|
||||
|
||||
func ApiUserRequiredActivity(h func(*Context, http.ResponseWriter, *http.Request), isUserActivity bool) http.Handler {
|
||||
return &handler{h, true, false, true, isUserActivity}
|
||||
}
|
||||
|
||||
func UserRequired(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler {
|
||||
return &handler{h, true, false, false, false}
|
||||
}
|
||||
|
||||
func ApiAdminSystemRequired(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler {
|
||||
return &handler{h, true, true, true, false}
|
||||
}
|
||||
|
||||
type handler struct {
|
||||
handleFunc func(*Context, http.ResponseWriter, *http.Request)
|
||||
requireUser bool
|
||||
requireSystemAdmin bool
|
||||
isApi bool
|
||||
isUserActivity bool
|
||||
}
|
||||
|
||||
func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
l4g.Debug("%v", r.URL.Path)
|
||||
|
||||
c := &Context{}
|
||||
c.RequestId = model.NewId()
|
||||
c.IpAddress = GetIpAddress(r)
|
||||
c.Path = r.URL.Path
|
||||
|
||||
protocol := "http"
|
||||
|
||||
// if the request came from the ELB then assume this is produciton
|
||||
// and redirect all http requests to https
|
||||
if utils.Cfg.ServiceSettings.UseSSL {
|
||||
forwardProto := r.Header.Get(model.HEADER_FORWARDED_PROTO)
|
||||
if forwardProto == "http" {
|
||||
l4g.Info("redirecting http request to https for %v", r.URL.Path)
|
||||
http.Redirect(w, r, "https://"+r.Host, http.StatusTemporaryRedirect)
|
||||
} else {
|
||||
protocol = "https"
|
||||
}
|
||||
}
|
||||
|
||||
c.TeamUrl = protocol + "://" + r.Host
|
||||
|
||||
w.Header().Set(model.HEADER_REQUEST_ID, c.RequestId)
|
||||
w.Header().Set(model.HEADER_VERSION_ID, utils.Cfg.ServiceSettings.Version)
|
||||
|
||||
sessionId := ""
|
||||
|
||||
// attempt to parse the session token from the header
|
||||
if ah := r.Header.Get(model.HEADER_AUTH); ah != "" {
|
||||
if len(ah) > 6 && strings.ToUpper(ah[0:6]) == "BEARER" {
|
||||
sessionId = ah[7:]
|
||||
}
|
||||
}
|
||||
|
||||
// attempt to parse the session token from the cookie
|
||||
if sessionId == "" {
|
||||
if cookie, err := r.Cookie(model.SESSION_TOKEN); err == nil {
|
||||
sessionId = cookie.Value
|
||||
}
|
||||
}
|
||||
|
||||
if sessionId != "" {
|
||||
|
||||
var session *model.Session
|
||||
if ts, ok := sessionCache.Get(sessionId); ok {
|
||||
session = ts.(*model.Session)
|
||||
}
|
||||
|
||||
if session == nil {
|
||||
if sessionResult := <-Srv.Store.Session().Get(sessionId); sessionResult.Err != nil {
|
||||
c.LogError(model.NewAppError("ServeHTTP", "Invalid session", "id="+sessionId+", err="+sessionResult.Err.DetailedError))
|
||||
} else {
|
||||
session = sessionResult.Data.(*model.Session)
|
||||
}
|
||||
}
|
||||
|
||||
if session == nil || session.IsExpired() {
|
||||
c.RemoveSessionCookie(w)
|
||||
c.Err = model.NewAppError("ServeHTTP", "Invalid or expired session, please login again.", "id="+sessionId)
|
||||
c.Err.StatusCode = http.StatusUnauthorized
|
||||
} else {
|
||||
c.Session = *session
|
||||
}
|
||||
}
|
||||
|
||||
if c.Err == nil && h.requireUser {
|
||||
c.UserRequired()
|
||||
}
|
||||
|
||||
if c.Err == nil && h.requireSystemAdmin {
|
||||
c.SystemAdminRequired()
|
||||
}
|
||||
|
||||
if c.Err == nil && h.isUserActivity && sessionId != "" && len(c.Session.UserId) > 0 {
|
||||
go func() {
|
||||
if err := (<-Srv.Store.User().UpdateUserAndSessionActivity(c.Session.UserId, sessionId, model.GetMillis())).Err; err != nil {
|
||||
l4g.Error("Failed to update LastActivityAt for user_id=%v and session_id=%v, err=%v", c.Session.UserId, sessionId, err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
if c.Err == nil {
|
||||
h.handleFunc(c, w, r)
|
||||
}
|
||||
|
||||
if c.Err != nil {
|
||||
c.Err.RequestId = c.RequestId
|
||||
c.LogError(c.Err)
|
||||
c.Err.Where = r.URL.Path
|
||||
|
||||
if h.isApi {
|
||||
w.WriteHeader(c.Err.StatusCode)
|
||||
w.Write([]byte(c.Err.ToJson()))
|
||||
} else {
|
||||
if c.Err.StatusCode == http.StatusUnauthorized {
|
||||
http.Redirect(w, r, "/?redirect="+url.QueryEscape(r.URL.Path), http.StatusTemporaryRedirect)
|
||||
} else {
|
||||
RenderWebError(c.Err, w, r)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Context) LogAudit(extraInfo string) {
|
||||
audit := &model.Audit{UserId: c.Session.UserId, IpAddress: c.IpAddress, Action: c.Path, ExtraInfo: extraInfo, SessionId: c.Session.AltId}
|
||||
Srv.Store.Audit().Save(audit)
|
||||
}
|
||||
|
||||
func (c *Context) LogAuditWithUserId(userId, extraInfo string) {
|
||||
|
||||
if len(c.Session.UserId) > 0 {
|
||||
extraInfo = strings.TrimSpace(extraInfo + " session_user=" + c.Session.UserId)
|
||||
}
|
||||
|
||||
audit := &model.Audit{UserId: userId, IpAddress: c.IpAddress, Action: c.Path, ExtraInfo: extraInfo, SessionId: c.Session.AltId}
|
||||
Srv.Store.Audit().Save(audit)
|
||||
}
|
||||
|
||||
func (c *Context) LogError(err *model.AppError) {
|
||||
l4g.Error("%v:%v code=%v rid=%v uid=%v ip=%v %v [details: %v]", c.Path, err.Where, err.StatusCode,
|
||||
c.RequestId, c.Session.UserId, c.IpAddress, err.Message, err.DetailedError)
|
||||
}
|
||||
|
||||
func (c *Context) UserRequired() {
|
||||
if len(c.Session.UserId) == 0 {
|
||||
c.Err = model.NewAppError("", "Invalid or expired session, please login again.", "UserRequired")
|
||||
c.Err.StatusCode = http.StatusUnauthorized
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Context) SystemAdminRequired() {
|
||||
if len(c.Session.UserId) == 0 {
|
||||
c.Err = model.NewAppError("", "Invalid or expired session, please login again.", "SystemAdminRequired")
|
||||
c.Err.StatusCode = http.StatusUnauthorized
|
||||
return
|
||||
} else if !c.IsSystemAdmin() {
|
||||
c.Err = model.NewAppError("", "You do not have the appropriate permissions", "AdminRequired")
|
||||
c.Err.StatusCode = http.StatusForbidden
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Context) HasPermissionsToUser(userId string, where string) bool {
|
||||
|
||||
// You are the user
|
||||
if c.Session.UserId == userId {
|
||||
return true
|
||||
}
|
||||
|
||||
// You're a mattermost system admin and you're on the VPN
|
||||
if c.IsSystemAdmin() {
|
||||
return true
|
||||
}
|
||||
|
||||
c.Err = model.NewAppError(where, "You do not have the appropriate permissions", "userId="+userId)
|
||||
c.Err.StatusCode = http.StatusForbidden
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *Context) HasPermissionsToTeam(teamId string, where string) bool {
|
||||
if c.Session.TeamId == teamId {
|
||||
return true
|
||||
}
|
||||
|
||||
// You're a mattermost system admin and you're on the VPN
|
||||
if c.IsSystemAdmin() {
|
||||
return true
|
||||
}
|
||||
|
||||
c.Err = model.NewAppError(where, "You do not have the appropriate permissions", "userId="+c.Session.UserId+", teamId="+teamId)
|
||||
c.Err.StatusCode = http.StatusForbidden
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *Context) HasPermissionsToChannel(sc store.StoreChannel, where string) bool {
|
||||
if cresult := <-sc; cresult.Err != nil {
|
||||
c.Err = cresult.Err
|
||||
return false
|
||||
} else if cresult.Data.(int64) != 1 {
|
||||
c.Err = model.NewAppError(where, "You do not have the appropriate permissions", "userId="+c.Session.UserId)
|
||||
c.Err.StatusCode = http.StatusForbidden
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *Context) IsSystemAdmin() bool {
|
||||
if strings.Contains(c.Session.Roles, model.ROLE_SYSTEM_ADMIN) && IsPrivateIpAddress(c.IpAddress) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *Context) RemoveSessionCookie(w http.ResponseWriter) {
|
||||
|
||||
sessionCache.Remove(c.Session.Id)
|
||||
|
||||
cookie := &http.Cookie{
|
||||
Name: model.SESSION_TOKEN,
|
||||
Value: "",
|
||||
Path: "/",
|
||||
MaxAge: -1,
|
||||
HttpOnly: true,
|
||||
}
|
||||
|
||||
http.SetCookie(w, cookie)
|
||||
}
|
||||
|
||||
func (c *Context) SetInvalidParam(where string, name string) {
|
||||
c.Err = model.NewAppError(where, "Invalid "+name+" parameter", "")
|
||||
c.Err.StatusCode = http.StatusBadRequest
|
||||
}
|
||||
|
||||
func (c *Context) SetUnknownError(where string, details string) {
|
||||
c.Err = model.NewAppError(where, "An unknown error has occured. Please contact support.", details)
|
||||
}
|
||||
|
||||
func GetIpAddress(r *http.Request) string {
|
||||
address := r.Header.Get(model.HEADER_FORWARDED)
|
||||
if len(address) == 0 {
|
||||
address, _, _ = net.SplitHostPort(r.RemoteAddr)
|
||||
}
|
||||
|
||||
return address
|
||||
}
|
||||
|
||||
func IsTestDomain(r *http.Request) bool {
|
||||
|
||||
if strings.Index(r.Host, "localhost") == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
if strings.Index(r.Host, "test") == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
if strings.Index(r.Host, "127.0.") == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
if strings.Index(r.Host, "192.168.") == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
if strings.Index(r.Host, "10.") == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
if strings.Index(r.Host, "176.") == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func IsBetaDomain(r *http.Request) bool {
|
||||
|
||||
if strings.Index(r.Host, "beta") == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
if strings.Index(r.Host, "ci") == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
var privateIpAddress = []*net.IPNet{
|
||||
&net.IPNet{IP: net.IPv4(10, 0, 0, 1), Mask: net.IPv4Mask(255, 0, 0, 0)},
|
||||
&net.IPNet{IP: net.IPv4(176, 16, 0, 1), Mask: net.IPv4Mask(255, 255, 0, 0)},
|
||||
&net.IPNet{IP: net.IPv4(192, 168, 0, 1), Mask: net.IPv4Mask(255, 255, 255, 0)},
|
||||
&net.IPNet{IP: net.IPv4(127, 0, 0, 1), Mask: net.IPv4Mask(255, 255, 255, 252)},
|
||||
}
|
||||
|
||||
func IsPrivateIpAddress(ipAddress string) bool {
|
||||
|
||||
for _, pips := range privateIpAddress {
|
||||
if pips.Contains(net.ParseIP(ipAddress)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func RenderWebError(err *model.AppError, w http.ResponseWriter, r *http.Request) {
|
||||
m := make(map[string]string)
|
||||
m["Message"] = err.Message
|
||||
m["Details"] = err.DetailedError
|
||||
m["SiteName"] = utils.Cfg.ServiceSettings.SiteName
|
||||
|
||||
w.WriteHeader(err.StatusCode)
|
||||
ServerTemplates.ExecuteTemplate(w, "error.html", m)
|
||||
}
|
||||
|
||||
func Handle404(w http.ResponseWriter, r *http.Request) {
|
||||
err := model.NewAppError("Handle404", "Sorry, we could not find the page.", "")
|
||||
err.StatusCode = http.StatusNotFound
|
||||
l4g.Error("%v: code=404 ip=%v", r.URL.Path, GetIpAddress(r))
|
||||
RenderWebError(err, w, r)
|
||||
}
|
||||
60
api/context_test.go
Обычный файл
60
api/context_test.go
Обычный файл
@@ -0,0 +1,60 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/mattermost/platform/model"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var ipAddressTests = []struct {
|
||||
address string
|
||||
expected bool
|
||||
}{
|
||||
{"126.255.255.255", false},
|
||||
{"127.0.0.1", true},
|
||||
{"127.0.0.4", false},
|
||||
{"9.255.255.255", false},
|
||||
{"10.0.0.1", true},
|
||||
{"11.0.0.1", false},
|
||||
{"176.15.155.255", false},
|
||||
{"176.16.0.1", true},
|
||||
{"176.31.0.1", false},
|
||||
{"192.167.255.255", false},
|
||||
{"192.168.0.1", true},
|
||||
{"192.169.0.1", false},
|
||||
}
|
||||
|
||||
func TestIpAddress(t *testing.T) {
|
||||
for _, v := range ipAddressTests {
|
||||
if IsPrivateIpAddress(v.address) != v.expected {
|
||||
t.Errorf("expect %v as %v", v.address, v.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestContext(t *testing.T) {
|
||||
context := Context{}
|
||||
|
||||
context.IpAddress = "127.0.0.1"
|
||||
context.Session.UserId = "5"
|
||||
|
||||
if !context.HasPermissionsToUser("5", "") {
|
||||
t.Fatal("should have permissions")
|
||||
}
|
||||
|
||||
if context.HasPermissionsToUser("6", "") {
|
||||
t.Fatal("shouldn't have permissions")
|
||||
}
|
||||
|
||||
context.Session.Roles = model.ROLE_SYSTEM_ADMIN
|
||||
if !context.HasPermissionsToUser("6", "") {
|
||||
t.Fatal("should have permissions")
|
||||
}
|
||||
|
||||
context.IpAddress = "125.0.0.1"
|
||||
if context.HasPermissionsToUser("6", "") {
|
||||
t.Fatal("shouldn't have permissions")
|
||||
}
|
||||
}
|
||||
375
api/file.go
Обычный файл
375
api/file.go
Обычный файл
@@ -0,0 +1,375 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
l4g "code.google.com/p/log4go"
|
||||
"fmt"
|
||||
"github.com/goamz/goamz/aws"
|
||||
"github.com/goamz/goamz/s3"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/mattermost/platform/model"
|
||||
"github.com/mattermost/platform/utils"
|
||||
"github.com/nfnt/resize"
|
||||
"image"
|
||||
_ "image/gif"
|
||||
"image/jpeg"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func InitFile(r *mux.Router) {
|
||||
l4g.Debug("Initializing post api routes")
|
||||
|
||||
sr := r.PathPrefix("/files").Subrouter()
|
||||
sr.Handle("/upload", ApiUserRequired(uploadFile)).Methods("POST")
|
||||
sr.Handle("/get/{channel_id:[A-Za-z0-9]+}/{user_id:[A-Za-z0-9]+}/{filename:([A-Za-z0-9]+/)?.+\\.[A-Za-z0-9]{3,}}", ApiAppHandler(getFile)).Methods("GET")
|
||||
sr.Handle("/get_public_link", ApiUserRequired(getPublicLink)).Methods("POST")
|
||||
}
|
||||
|
||||
func uploadFile(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
if !utils.IsS3Configured() {
|
||||
c.Err = model.NewAppError("uploadFile", "Unable to upload file. Amazon S3 not configured. ", "")
|
||||
c.Err.StatusCode = http.StatusNotImplemented
|
||||
return
|
||||
}
|
||||
|
||||
err := r.ParseMultipartForm(model.MAX_FILE_SIZE)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
var auth aws.Auth
|
||||
auth.AccessKey = utils.Cfg.AWSSettings.S3AccessKeyId
|
||||
auth.SecretKey = utils.Cfg.AWSSettings.S3SecretAccessKey
|
||||
|
||||
s := s3.New(auth, aws.Regions[utils.Cfg.AWSSettings.S3Region])
|
||||
bucket := s.Bucket(utils.Cfg.AWSSettings.S3Bucket)
|
||||
|
||||
m := r.MultipartForm
|
||||
|
||||
props := m.Value
|
||||
|
||||
if len(props["channel_id"]) == 0 {
|
||||
c.SetInvalidParam("uploadFile", "channel_id")
|
||||
return
|
||||
}
|
||||
channelId := props["channel_id"][0]
|
||||
if len(channelId) == 0 {
|
||||
c.SetInvalidParam("uploadFile", "channel_id")
|
||||
return
|
||||
}
|
||||
|
||||
cchan := Srv.Store.Channel().CheckPermissionsTo(c.Session.TeamId, channelId, c.Session.UserId)
|
||||
|
||||
files := m.File["files"]
|
||||
|
||||
resStruct := &model.FileUploadResponse{
|
||||
Filenames: []string{}}
|
||||
|
||||
imageNameList := []string{}
|
||||
imageDataList := [][]byte{}
|
||||
|
||||
if !c.HasPermissionsToChannel(cchan, "uploadFile") {
|
||||
return
|
||||
}
|
||||
|
||||
for i, _ := range files {
|
||||
file, err := files[i].Open()
|
||||
defer file.Close()
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
buf := bytes.NewBuffer(nil)
|
||||
io.Copy(buf, file)
|
||||
|
||||
ext := filepath.Ext(files[i].Filename)
|
||||
|
||||
uid := model.NewId()
|
||||
|
||||
path := "teams/" + c.Session.TeamId + "/channels/" + channelId + "/users/" + c.Session.UserId + "/" + uid + "/" + files[i].Filename
|
||||
|
||||
if model.IsFileExtImage(ext) {
|
||||
options := s3.Options{}
|
||||
err = bucket.Put(path, buf.Bytes(), model.GetImageMimeType(ext), s3.Private, options)
|
||||
imageNameList = append(imageNameList, uid+"/"+files[i].Filename)
|
||||
imageDataList = append(imageDataList, buf.Bytes())
|
||||
} else {
|
||||
options := s3.Options{}
|
||||
err = bucket.Put(path, buf.Bytes(), "binary/octet-stream", s3.Private, options)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
c.Err = model.NewAppError("uploadFile", "Unable to upload file. ", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
fileUrl := c.TeamUrl + "/api/v1/files/get/" + channelId + "/" + c.Session.UserId + "/" + uid + "/" + files[i].Filename
|
||||
resStruct.Filenames = append(resStruct.Filenames, fileUrl)
|
||||
}
|
||||
|
||||
fireAndForgetHandleImages(imageNameList, imageDataList, c.Session.TeamId, channelId, c.Session.UserId)
|
||||
|
||||
w.Write([]byte(resStruct.ToJson()))
|
||||
}
|
||||
|
||||
func fireAndForgetHandleImages(filenames []string, fileData [][]byte, teamId, channelId, userId string) {
|
||||
|
||||
go func() {
|
||||
var auth aws.Auth
|
||||
auth.AccessKey = utils.Cfg.AWSSettings.S3AccessKeyId
|
||||
auth.SecretKey = utils.Cfg.AWSSettings.S3SecretAccessKey
|
||||
|
||||
s := s3.New(auth, aws.Regions[utils.Cfg.AWSSettings.S3Region])
|
||||
bucket := s.Bucket(utils.Cfg.AWSSettings.S3Bucket)
|
||||
|
||||
dest := "teams/" + teamId + "/channels/" + channelId + "/users/" + userId + "/"
|
||||
|
||||
for i, filename := range filenames {
|
||||
name := filename[:strings.LastIndex(filename, ".")]
|
||||
go func() {
|
||||
// Decode image bytes into Image object
|
||||
img, _, err := image.Decode(bytes.NewReader(fileData[i]))
|
||||
if err != nil {
|
||||
l4g.Error("Unable to decode image channelId=%v userId=%v filename=%v err=%v", channelId, userId, filename, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Decode image config
|
||||
imgConfig, _, err := image.DecodeConfig(bytes.NewReader(fileData[i]))
|
||||
if err != nil {
|
||||
l4g.Error("Unable to decode image config channelId=%v userId=%v filename=%v err=%v", channelId, userId, filename, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Create thumbnail
|
||||
go func() {
|
||||
var thumbnail image.Image
|
||||
if imgConfig.Width > int(utils.Cfg.ImageSettings.ThumbnailWidth) {
|
||||
thumbnail = resize.Resize(utils.Cfg.ImageSettings.ThumbnailWidth, utils.Cfg.ImageSettings.ThumbnailHeight, img, resize.NearestNeighbor)
|
||||
} else {
|
||||
thumbnail = img
|
||||
}
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
err = jpeg.Encode(buf, thumbnail, &jpeg.Options{Quality: 90})
|
||||
if err != nil {
|
||||
l4g.Error("Unable to encode image as jpeg channelId=%v userId=%v filename=%v err=%v", channelId, userId, filename, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Upload thumbnail to S3
|
||||
options := s3.Options{}
|
||||
err = bucket.Put(dest+name+"_thumb.jpg", buf.Bytes(), "image/jpeg", s3.Private, options)
|
||||
if err != nil {
|
||||
l4g.Error("Unable to upload thumbnail to S3 channelId=%v userId=%v filename=%v err=%v", channelId, userId, filename, err)
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
// Create preview
|
||||
go func() {
|
||||
var preview image.Image
|
||||
if imgConfig.Width > int(utils.Cfg.ImageSettings.PreviewWidth) {
|
||||
preview = resize.Resize(utils.Cfg.ImageSettings.PreviewWidth, utils.Cfg.ImageSettings.PreviewHeight, img, resize.NearestNeighbor)
|
||||
} else {
|
||||
preview = img
|
||||
}
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
err = jpeg.Encode(buf, preview, &jpeg.Options{Quality: 90})
|
||||
|
||||
//err = png.Encode(buf, preview)
|
||||
if err != nil {
|
||||
l4g.Error("Unable to encode image as preview jpg channelId=%v userId=%v filename=%v err=%v", channelId, userId, filename, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Upload preview to S3
|
||||
options := s3.Options{}
|
||||
err = bucket.Put(dest+name+"_preview.jpg", buf.Bytes(), "image/jpeg", s3.Private, options)
|
||||
if err != nil {
|
||||
l4g.Error("Unable to upload preview to S3 channelId=%v userId=%v filename=%v err=%v", channelId, userId, filename, err)
|
||||
return
|
||||
}
|
||||
}()
|
||||
}()
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
type ImageGetResult struct {
|
||||
Error error
|
||||
ImageData []byte
|
||||
}
|
||||
|
||||
func getFile(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
if !utils.IsS3Configured() {
|
||||
c.Err = model.NewAppError("getFile", "Unable to get file. Amazon S3 not configured. ", "")
|
||||
c.Err.StatusCode = http.StatusNotImplemented
|
||||
return
|
||||
}
|
||||
|
||||
params := mux.Vars(r)
|
||||
|
||||
channelId := params["channel_id"]
|
||||
if len(channelId) != 26 {
|
||||
c.SetInvalidParam("getFile", "channel_id")
|
||||
return
|
||||
}
|
||||
|
||||
userId := params["user_id"]
|
||||
if len(userId) != 26 {
|
||||
c.SetInvalidParam("getFile", "user_id")
|
||||
return
|
||||
}
|
||||
|
||||
filename := params["filename"]
|
||||
if len(filename) == 0 {
|
||||
c.SetInvalidParam("getFile", "filename")
|
||||
return
|
||||
}
|
||||
|
||||
hash := r.URL.Query().Get("h")
|
||||
data := r.URL.Query().Get("d")
|
||||
teamId := r.URL.Query().Get("t")
|
||||
|
||||
cchan := Srv.Store.Channel().CheckPermissionsTo(c.Session.TeamId, channelId, c.Session.UserId)
|
||||
|
||||
var auth aws.Auth
|
||||
auth.AccessKey = utils.Cfg.AWSSettings.S3AccessKeyId
|
||||
auth.SecretKey = utils.Cfg.AWSSettings.S3SecretAccessKey
|
||||
|
||||
s := s3.New(auth, aws.Regions[utils.Cfg.AWSSettings.S3Region])
|
||||
bucket := s.Bucket(utils.Cfg.AWSSettings.S3Bucket)
|
||||
|
||||
path := ""
|
||||
if len(teamId) == 26 {
|
||||
path = "teams/" + teamId + "/channels/" + channelId + "/users/" + userId + "/" + filename
|
||||
} else {
|
||||
path = "teams/" + c.Session.TeamId + "/channels/" + channelId + "/users/" + userId + "/" + filename
|
||||
}
|
||||
|
||||
fileData := make(chan []byte)
|
||||
asyncGetFile(bucket, path, fileData)
|
||||
|
||||
if len(hash) > 0 && len(data) > 0 && len(teamId) == 26 {
|
||||
if !model.ComparePassword(hash, fmt.Sprintf("%v:%v", data, utils.Cfg.ServiceSettings.PublicLinkSalt)) {
|
||||
c.Err = model.NewAppError("getFile", "The public link does not appear to be valid", "")
|
||||
return
|
||||
}
|
||||
props := model.MapFromJson(strings.NewReader(data))
|
||||
|
||||
t, err := strconv.ParseInt(props["time"], 10, 64)
|
||||
if err != nil || model.GetMillis()-t > 1000*60*60*24*7 { // one week
|
||||
c.Err = model.NewAppError("getFile", "The public link has expired", "")
|
||||
return
|
||||
}
|
||||
} else if !c.HasPermissionsToChannel(cchan, "getFile") {
|
||||
return
|
||||
}
|
||||
|
||||
f := <-fileData
|
||||
|
||||
if f == nil {
|
||||
var f2 []byte
|
||||
tries := 0
|
||||
for {
|
||||
time.Sleep(3000 * time.Millisecond)
|
||||
tries++
|
||||
|
||||
asyncGetFile(bucket, path, fileData)
|
||||
f2 = <-fileData
|
||||
|
||||
if f2 != nil {
|
||||
w.Header().Set("Cache-Control", "max-age=2592000, public")
|
||||
w.Header().Set("Content-Length", strconv.Itoa(len(f2)))
|
||||
w.Write(f2)
|
||||
return
|
||||
} else if tries >= 2 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
c.Err = model.NewAppError("getFile", "Could not find file.", "url extenstion: "+path)
|
||||
c.Err.StatusCode = http.StatusNotFound
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Cache-Control", "max-age=2592000, public")
|
||||
w.Header().Set("Content-Length", strconv.Itoa(len(f)))
|
||||
w.Write(f)
|
||||
}
|
||||
|
||||
func asyncGetFile(bucket *s3.Bucket, path string, fileData chan []byte) {
|
||||
go func() {
|
||||
data, getErr := bucket.Get(path)
|
||||
if getErr != nil {
|
||||
fileData <- nil
|
||||
} else {
|
||||
fileData <- data
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func getPublicLink(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
if !utils.Cfg.TeamSettings.AllowPublicLink {
|
||||
c.Err = model.NewAppError("getPublicLink", "Public links have been disabled", "")
|
||||
c.Err.StatusCode = http.StatusForbidden
|
||||
}
|
||||
|
||||
if !utils.IsS3Configured() {
|
||||
c.Err = model.NewAppError("getPublicLink", "Unable to get link. Amazon S3 not configured. ", "")
|
||||
c.Err.StatusCode = http.StatusNotImplemented
|
||||
return
|
||||
}
|
||||
|
||||
props := model.MapFromJson(r.Body)
|
||||
|
||||
filename := props["filename"]
|
||||
if len(filename) == 0 {
|
||||
c.SetInvalidParam("getPublicLink", "filename")
|
||||
return
|
||||
}
|
||||
|
||||
matches := model.PartialUrlRegex.FindAllStringSubmatch(filename, -1)
|
||||
if len(matches) == 0 || len(matches[0]) < 5 {
|
||||
c.SetInvalidParam("getPublicLink", "filename")
|
||||
return
|
||||
}
|
||||
|
||||
getType := matches[0][1]
|
||||
channelId := matches[0][2]
|
||||
userId := matches[0][3]
|
||||
filename = matches[0][4]
|
||||
|
||||
cchan := Srv.Store.Channel().CheckPermissionsTo(c.Session.TeamId, channelId, c.Session.UserId)
|
||||
|
||||
newProps := make(map[string]string)
|
||||
newProps["filename"] = filename
|
||||
newProps["time"] = fmt.Sprintf("%v", model.GetMillis())
|
||||
|
||||
data := model.MapToJson(newProps)
|
||||
hash := model.HashPassword(fmt.Sprintf("%v:%v", data, utils.Cfg.ServiceSettings.PublicLinkSalt))
|
||||
|
||||
url := fmt.Sprintf("%s/api/v1/files/%s/%s/%s/%s?d=%s&h=%s&t=%s", c.TeamUrl, getType, channelId, userId, filename, url.QueryEscape(data), url.QueryEscape(hash), c.Session.TeamId)
|
||||
|
||||
if !c.HasPermissionsToChannel(cchan, "getPublicLink") {
|
||||
return
|
||||
}
|
||||
|
||||
rData := make(map[string]string)
|
||||
rData["public_link"] = url
|
||||
|
||||
w.Write([]byte(model.MapToJson(rData)))
|
||||
}
|
||||
77
api/file_benchmark_test.go
Обычный файл
77
api/file_benchmark_test.go
Обычный файл
@@ -0,0 +1,77 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/mattermost/platform/model"
|
||||
"github.com/mattermost/platform/utils"
|
||||
"net/url"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func BenchmarkUploadFile(b *testing.B) {
|
||||
_, _, channel := SetupBenchmark()
|
||||
|
||||
testPoster := NewAutoPostCreator(Client, channel.Id)
|
||||
|
||||
// Benchmark Start
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
testPoster.UploadTestFile()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkGetFile(b *testing.B) {
|
||||
team, _, channel := SetupBenchmark()
|
||||
|
||||
testPoster := NewAutoPostCreator(Client, channel.Id)
|
||||
filenames, err := testPoster.UploadTestFile()
|
||||
if err == false {
|
||||
b.Fatal("Unable to upload file for benchmark")
|
||||
}
|
||||
|
||||
newProps := make(map[string]string)
|
||||
newProps["filename"] = filenames[0]
|
||||
newProps["time"] = fmt.Sprintf("%v", model.GetMillis())
|
||||
|
||||
data := model.MapToJson(newProps)
|
||||
hash := model.HashPassword(fmt.Sprintf("%v:%v", data, utils.Cfg.ServiceSettings.PublicLinkSalt))
|
||||
|
||||
// wait a bit for files to ready
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
// Benchmark Start
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if _, downErr := Client.GetFile(filenames[0]+"?d="+url.QueryEscape(data)+"&h="+url.QueryEscape(hash)+"&t="+team.Id, true); downErr != nil {
|
||||
b.Fatal(downErr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkGetPublicLink(b *testing.B) {
|
||||
_, _, channel := SetupBenchmark()
|
||||
|
||||
testPoster := NewAutoPostCreator(Client, channel.Id)
|
||||
filenames, err := testPoster.UploadTestFile()
|
||||
if err == false {
|
||||
b.Fatal("Unable to upload file for benchmark")
|
||||
}
|
||||
|
||||
data := make(map[string]string)
|
||||
data["filename"] = filenames[0]
|
||||
|
||||
// wait a bit for files to ready
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
// Benchmark Start
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if _, downErr := Client.GetPublicLink(data); downErr != nil {
|
||||
b.Fatal(downErr)
|
||||
}
|
||||
}
|
||||
}
|
||||
379
api/file_test.go
Обычный файл
379
api/file_test.go
Обычный файл
@@ -0,0 +1,379 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/goamz/goamz/aws"
|
||||
"github.com/goamz/goamz/s3"
|
||||
"github.com/mattermost/platform/model"
|
||||
"github.com/mattermost/platform/utils"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestUploadFile(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
|
||||
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(user1.Id)
|
||||
|
||||
Client.LoginByEmail(team.Domain, user1.Email, "pwd")
|
||||
|
||||
channel1 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
|
||||
|
||||
body := &bytes.Buffer{}
|
||||
writer := multipart.NewWriter(body)
|
||||
part, err := writer.CreateFormFile("files", "test.png")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
path := utils.FindDir("web/static/images")
|
||||
file, err := os.Open(path + "/test.png")
|
||||
defer file.Close()
|
||||
|
||||
_, err = io.Copy(part, file)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
field, err := writer.CreateFormField("channel_id")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = field.Write([]byte(channel1.Id))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = writer.Close()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
resp, appErr := Client.UploadFile("/files/upload", body.Bytes(), writer.FormDataContentType())
|
||||
if utils.IsS3Configured() {
|
||||
if appErr != nil {
|
||||
t.Fatal(appErr)
|
||||
}
|
||||
|
||||
filenames := resp.Data.(*model.FileUploadResponse).Filenames
|
||||
|
||||
var auth aws.Auth
|
||||
auth.AccessKey = utils.Cfg.AWSSettings.S3AccessKeyId
|
||||
auth.SecretKey = utils.Cfg.AWSSettings.S3SecretAccessKey
|
||||
|
||||
s := s3.New(auth, aws.Regions[utils.Cfg.AWSSettings.S3Region])
|
||||
bucket := s.Bucket(utils.Cfg.AWSSettings.S3Bucket)
|
||||
|
||||
fileId := strings.Split(filenames[0], ".")[0]
|
||||
|
||||
// wait a bit for files to ready
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
err = bucket.Del("teams/" + team.Id + "/channels/" + channel1.Id + "/users/" + user1.Id + "/" + filenames[0])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = bucket.Del("teams/" + team.Id + "/channels/" + channel1.Id + "/users/" + user1.Id + "/" + fileId + "_thumb.jpg")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = bucket.Del("teams/" + team.Id + "/channels/" + channel1.Id + "/users/" + user1.Id + "/" + fileId + "_preview.png")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
} else {
|
||||
if appErr == nil {
|
||||
t.Fatal("S3 not configured, should have failed")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetFile(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
|
||||
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(user1.Id)
|
||||
|
||||
Client.LoginByEmail(team.Domain, user1.Email, "pwd")
|
||||
|
||||
channel1 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
|
||||
|
||||
if utils.IsS3Configured() {
|
||||
|
||||
body := &bytes.Buffer{}
|
||||
writer := multipart.NewWriter(body)
|
||||
part, err := writer.CreateFormFile("files", "test.png")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
path := utils.FindDir("web/static/images")
|
||||
file, err := os.Open(path + "/test.png")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
_, err = io.Copy(part, file)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
field, err := writer.CreateFormField("channel_id")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = field.Write([]byte(channel1.Id))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = writer.Close()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
resp, upErr := Client.UploadFile("/files/upload", body.Bytes(), writer.FormDataContentType())
|
||||
if upErr != nil {
|
||||
t.Fatal(upErr)
|
||||
}
|
||||
|
||||
filenames := resp.Data.(*model.FileUploadResponse).Filenames
|
||||
|
||||
// wait a bit for files to ready
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
if _, downErr := Client.GetFile(filenames[0], true); downErr != nil {
|
||||
t.Fatal("file get failed")
|
||||
}
|
||||
|
||||
team2 := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team2 = Client.Must(Client.CreateTeam(team2)).Data.(*model.Team)
|
||||
|
||||
user2 := &model.User{TeamId: team2.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(user2.Id)
|
||||
|
||||
newProps := make(map[string]string)
|
||||
newProps["filename"] = filenames[0]
|
||||
newProps["time"] = fmt.Sprintf("%v", model.GetMillis())
|
||||
|
||||
data := model.MapToJson(newProps)
|
||||
hash := model.HashPassword(fmt.Sprintf("%v:%v", data, utils.Cfg.ServiceSettings.PublicLinkSalt))
|
||||
|
||||
Client.LoginByEmail(team2.Domain, user2.Email, "pwd")
|
||||
|
||||
if _, downErr := Client.GetFile(filenames[0]+"?d="+url.QueryEscape(data)+"&h="+url.QueryEscape(hash)+"&t="+team.Id, true); downErr != nil {
|
||||
t.Fatal(downErr)
|
||||
}
|
||||
|
||||
if _, downErr := Client.GetFile(filenames[0]+"?d="+url.QueryEscape(data)+"&h="+url.QueryEscape(hash), true); downErr == nil {
|
||||
t.Fatal("Should have errored - missing team id")
|
||||
}
|
||||
|
||||
if _, downErr := Client.GetFile(filenames[0]+"?d="+url.QueryEscape(data)+"&h="+url.QueryEscape(hash)+"&t=junk", true); downErr == nil {
|
||||
t.Fatal("Should have errored - bad team id")
|
||||
}
|
||||
|
||||
if _, downErr := Client.GetFile(filenames[0]+"?d="+url.QueryEscape(data)+"&h="+url.QueryEscape(hash)+"&t=12345678901234567890123456", true); downErr == nil {
|
||||
t.Fatal("Should have errored - bad team id")
|
||||
}
|
||||
|
||||
if _, downErr := Client.GetFile(filenames[0]+"?d="+url.QueryEscape(data)+"&t="+team.Id, true); downErr == nil {
|
||||
t.Fatal("Should have errored - missing hash")
|
||||
}
|
||||
|
||||
if _, downErr := Client.GetFile(filenames[0]+"?d="+url.QueryEscape(data)+"&h=junk&t="+team.Id, true); downErr == nil {
|
||||
t.Fatal("Should have errored - bad hash")
|
||||
}
|
||||
|
||||
if _, downErr := Client.GetFile(filenames[0]+"?h="+url.QueryEscape(hash)+"&t="+team.Id, true); downErr == nil {
|
||||
t.Fatal("Should have errored - missing data")
|
||||
}
|
||||
|
||||
if _, downErr := Client.GetFile(filenames[0]+"?d=junk&h="+url.QueryEscape(hash)+"&t="+team.Id, true); downErr == nil {
|
||||
t.Fatal("Should have errored - bad data")
|
||||
}
|
||||
|
||||
if _, downErr := Client.GetFile(filenames[0], true); downErr == nil {
|
||||
t.Fatal("Should have errored - user not logged in and link not public")
|
||||
}
|
||||
|
||||
var auth aws.Auth
|
||||
auth.AccessKey = utils.Cfg.AWSSettings.S3AccessKeyId
|
||||
auth.SecretKey = utils.Cfg.AWSSettings.S3SecretAccessKey
|
||||
|
||||
s := s3.New(auth, aws.Regions[utils.Cfg.AWSSettings.S3Region])
|
||||
bucket := s.Bucket(utils.Cfg.AWSSettings.S3Bucket)
|
||||
|
||||
fileId := strings.Split(filenames[0], ".")[0]
|
||||
|
||||
err = bucket.Del("teams/" + team.Id + "/channels/" + channel1.Id + "/users/" + user1.Id + "/" + filenames[0])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = bucket.Del("teams/" + team.Id + "/channels/" + channel1.Id + "/users/" + user1.Id + "/" + fileId + "_thumb.jpg")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = bucket.Del("teams/" + team.Id + "/channels/" + channel1.Id + "/users/" + user1.Id + "/" + fileId + "_preview.png")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
} else {
|
||||
if _, downErr := Client.GetFile("/files/get/yxebdmbz5pgupx7q6ez88rw11a/n3btzxu9hbnapqk36iwaxkjxhc/junk.jpg", false); downErr.StatusCode != http.StatusNotImplemented {
|
||||
t.Fatal("Status code should have been 501 - Not Implemented")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetPublicLink(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
|
||||
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(user1.Id)
|
||||
|
||||
user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(user2.Id)
|
||||
|
||||
Client.LoginByEmail(team.Domain, user1.Email, "pwd")
|
||||
|
||||
channel1 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
|
||||
|
||||
if utils.IsS3Configured() {
|
||||
|
||||
body := &bytes.Buffer{}
|
||||
writer := multipart.NewWriter(body)
|
||||
part, err := writer.CreateFormFile("files", "test.png")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
path := utils.FindDir("web/static/images")
|
||||
file, err := os.Open(path + "/test.png")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
_, err = io.Copy(part, file)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
field, err := writer.CreateFormField("channel_id")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = field.Write([]byte(channel1.Id))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = writer.Close()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
resp, upErr := Client.UploadFile("/files/upload", body.Bytes(), writer.FormDataContentType())
|
||||
if upErr != nil {
|
||||
t.Fatal(upErr)
|
||||
}
|
||||
|
||||
filenames := resp.Data.(*model.FileUploadResponse).Filenames
|
||||
|
||||
post1 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a", Filenames: filenames}
|
||||
|
||||
rpost1, postErr := Client.CreatePost(post1)
|
||||
if postErr != nil {
|
||||
t.Fatal(postErr)
|
||||
}
|
||||
|
||||
if rpost1.Data.(*model.Post).Filenames[0] != filenames[0] {
|
||||
t.Fatal("filenames don't match")
|
||||
}
|
||||
|
||||
// wait a bit for files to ready
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
data := make(map[string]string)
|
||||
data["filename"] = filenames[0]
|
||||
|
||||
if _, err := Client.GetPublicLink(data); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
data["filename"] = "junk"
|
||||
|
||||
if _, err := Client.GetPublicLink(data); err == nil {
|
||||
t.Fatal("Should have errored - bad file path")
|
||||
}
|
||||
|
||||
Client.LoginByEmail(team.Domain, user2.Email, "pwd")
|
||||
data["filename"] = filenames[0]
|
||||
if _, err := Client.GetPublicLink(data); err == nil {
|
||||
t.Fatal("should have errored, user not member of channel")
|
||||
}
|
||||
|
||||
// perform clean-up on s3
|
||||
var auth aws.Auth
|
||||
auth.AccessKey = utils.Cfg.AWSSettings.S3AccessKeyId
|
||||
auth.SecretKey = utils.Cfg.AWSSettings.S3SecretAccessKey
|
||||
|
||||
s := s3.New(auth, aws.Regions[utils.Cfg.AWSSettings.S3Region])
|
||||
bucket := s.Bucket(utils.Cfg.AWSSettings.S3Bucket)
|
||||
|
||||
fileId := strings.Split(filenames[0], ".")[0]
|
||||
|
||||
if err = bucket.Del("teams/" + team.Id + "/channels/" + channel1.Id + "/users/" + rpost1.Data.(*model.Post).UserId + "/" + filenames[0]); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err = bucket.Del("teams/" + team.Id + "/channels/" + channel1.Id + "/users/" + rpost1.Data.(*model.Post).UserId + "/" + fileId + "_thumb.jpg"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err = bucket.Del("teams/" + team.Id + "/channels/" + channel1.Id + "/users/" + rpost1.Data.(*model.Post).UserId + "/" + fileId + "_preview.png"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
} else {
|
||||
data := make(map[string]string)
|
||||
if _, err := Client.GetPublicLink(data); err.StatusCode != http.StatusNotImplemented {
|
||||
t.Fatal("Status code should have been 501 - Not Implemented")
|
||||
}
|
||||
}
|
||||
}
|
||||
692
api/post.go
Обычный файл
692
api/post.go
Обычный файл
@@ -0,0 +1,692 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
l4g "code.google.com/p/log4go"
|
||||
"fmt"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/mattermost/platform/model"
|
||||
"github.com/mattermost/platform/store"
|
||||
"github.com/mattermost/platform/utils"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func InitPost(r *mux.Router) {
|
||||
l4g.Debug("Initializing post api routes")
|
||||
|
||||
r.Handle("/posts/search", ApiUserRequired(searchPosts)).Methods("GET")
|
||||
|
||||
sr := r.PathPrefix("/channels/{id:[A-Za-z0-9]+}").Subrouter()
|
||||
sr.Handle("/create", ApiUserRequired(createPost)).Methods("POST")
|
||||
sr.Handle("/valet_create", ApiUserRequired(createValetPost)).Methods("POST")
|
||||
sr.Handle("/update", ApiUserRequired(updatePost)).Methods("POST")
|
||||
sr.Handle("/posts/{offset:[0-9]+}/{limit:[0-9]+}", ApiUserRequiredActivity(getPosts, false)).Methods("GET")
|
||||
sr.Handle("/post/{post_id:[A-Za-z0-9]+}", ApiUserRequired(getPost)).Methods("GET")
|
||||
sr.Handle("/post/{post_id:[A-Za-z0-9]+}/delete", ApiUserRequired(deletePost)).Methods("POST")
|
||||
}
|
||||
|
||||
func createPost(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
post := model.PostFromJson(r.Body)
|
||||
if post == nil {
|
||||
c.SetInvalidParam("createPost", "post")
|
||||
return
|
||||
}
|
||||
|
||||
// Create and save post object to channel
|
||||
cchan := Srv.Store.Channel().CheckPermissionsTo(c.Session.TeamId, post.ChannelId, c.Session.UserId)
|
||||
|
||||
if !c.HasPermissionsToChannel(cchan, "createPost") {
|
||||
return
|
||||
}
|
||||
|
||||
if rp, err := CreatePost(c, post, true); err != nil {
|
||||
c.Err = err
|
||||
|
||||
if strings.Contains(c.Err.Message, "parameter") {
|
||||
c.Err.StatusCode = http.StatusBadRequest
|
||||
}
|
||||
|
||||
return
|
||||
} else {
|
||||
w.Write([]byte(rp.ToJson()))
|
||||
}
|
||||
}
|
||||
|
||||
func createValetPost(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
post := model.PostFromJson(r.Body)
|
||||
if post == nil {
|
||||
c.SetInvalidParam("createValetPost", "post")
|
||||
return
|
||||
}
|
||||
|
||||
// Any one with access to the team can post as valet to any open channel
|
||||
cchan := Srv.Store.Channel().CheckOpenChannelPermissions(c.Session.TeamId, post.ChannelId)
|
||||
|
||||
if !c.HasPermissionsToChannel(cchan, "createValetPost") {
|
||||
return
|
||||
}
|
||||
|
||||
if rp, err := CreateValetPost(c, post); err != nil {
|
||||
c.Err = err
|
||||
|
||||
if strings.Contains(c.Err.Message, "parameter") {
|
||||
c.Err.StatusCode = http.StatusBadRequest
|
||||
}
|
||||
|
||||
return
|
||||
} else {
|
||||
w.Write([]byte(rp.ToJson()))
|
||||
}
|
||||
}
|
||||
|
||||
func CreateValetPost(c *Context, post *model.Post) (*model.Post, *model.AppError) {
|
||||
post.Hashtags, _ = model.ParseHashtags(post.Message)
|
||||
|
||||
post.Filenames = []string{} // no files allowed in valet posts yet
|
||||
|
||||
if result := <-Srv.Store.User().GetByUsername(c.Session.TeamId, "valet"); result.Err != nil {
|
||||
// if the bot doesn't exist, create it
|
||||
if tresult := <-Srv.Store.Team().Get(c.Session.TeamId); tresult.Err != nil {
|
||||
return nil, tresult.Err
|
||||
} else {
|
||||
post.UserId = (CreateValet(c, tresult.Data.(*model.Team))).Id
|
||||
}
|
||||
} else {
|
||||
post.UserId = result.Data.(*model.User).Id
|
||||
}
|
||||
|
||||
var rpost *model.Post
|
||||
if result := <-Srv.Store.Post().Save(post); result.Err != nil {
|
||||
return nil, result.Err
|
||||
} else {
|
||||
rpost = result.Data.(*model.Post)
|
||||
}
|
||||
|
||||
fireAndForgetNotifications(rpost, c.Session.TeamId, c.TeamUrl)
|
||||
|
||||
return rpost, nil
|
||||
}
|
||||
|
||||
func CreatePost(c *Context, post *model.Post, doUpdateLastViewed bool) (*model.Post, *model.AppError) {
|
||||
var pchan store.StoreChannel
|
||||
if len(post.RootId) > 0 {
|
||||
pchan = Srv.Store.Post().Get(post.RootId)
|
||||
}
|
||||
|
||||
// Verify the parent/child relationships are correct
|
||||
if pchan != nil {
|
||||
if presult := <-pchan; presult.Err != nil {
|
||||
return nil, model.NewAppError("createPost", "Invalid RootId parameter", "")
|
||||
} else {
|
||||
list := presult.Data.(*model.PostList)
|
||||
if len(list.Posts) == 0 || !list.IsChannelId(post.ChannelId) {
|
||||
return nil, model.NewAppError("createPost", "Invalid ChannelId for RootId parameter", "")
|
||||
}
|
||||
|
||||
if post.ParentId == "" {
|
||||
post.ParentId = post.RootId
|
||||
}
|
||||
|
||||
if post.RootId != post.ParentId {
|
||||
parent := list.Posts[post.ParentId]
|
||||
if parent == nil {
|
||||
return nil, model.NewAppError("createPost", "Invalid ParentId parameter", "")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
post.Hashtags, _ = model.ParseHashtags(post.Message)
|
||||
|
||||
post.UserId = c.Session.UserId
|
||||
|
||||
if len(post.Filenames) > 0 {
|
||||
doRemove := false
|
||||
for i := len(post.Filenames) - 1; i >= 0; i-- {
|
||||
path := post.Filenames[i]
|
||||
|
||||
doRemove = false
|
||||
if model.UrlRegex.MatchString(path) {
|
||||
continue
|
||||
} else if model.PartialUrlRegex.MatchString(path) {
|
||||
matches := model.PartialUrlRegex.FindAllStringSubmatch(path, -1)
|
||||
if len(matches) == 0 || len(matches[0]) < 5 {
|
||||
doRemove = true
|
||||
}
|
||||
|
||||
channelId := matches[0][2]
|
||||
if channelId != post.ChannelId {
|
||||
doRemove = true
|
||||
}
|
||||
|
||||
userId := matches[0][3]
|
||||
if userId != post.UserId {
|
||||
doRemove = true
|
||||
}
|
||||
} else {
|
||||
doRemove = true
|
||||
}
|
||||
if doRemove {
|
||||
l4g.Error("Bad filename discarded, filename=%v", path)
|
||||
post.Filenames = append(post.Filenames[:i], post.Filenames[i+1:]...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var rpost *model.Post
|
||||
if result := <-Srv.Store.Post().Save(post); result.Err != nil {
|
||||
return nil, result.Err
|
||||
} else if doUpdateLastViewed && (<-Srv.Store.Channel().UpdateLastViewedAt(post.ChannelId, c.Session.UserId)).Err != nil {
|
||||
return nil, result.Err
|
||||
} else {
|
||||
rpost = result.Data.(*model.Post)
|
||||
|
||||
fireAndForgetNotifications(rpost, c.Session.TeamId, c.TeamUrl)
|
||||
|
||||
}
|
||||
|
||||
return rpost, nil
|
||||
}
|
||||
|
||||
func fireAndForgetNotifications(post *model.Post, teamId, teamUrl string) {
|
||||
|
||||
go func() {
|
||||
// Get a list of user names (to be used as keywords) and ids for the given team
|
||||
uchan := Srv.Store.User().GetProfiles(teamId)
|
||||
echan := Srv.Store.Channel().GetMembers(post.ChannelId)
|
||||
cchan := Srv.Store.Channel().Get(post.ChannelId)
|
||||
tchan := Srv.Store.Team().Get(teamId)
|
||||
|
||||
var channel *model.Channel
|
||||
var channelName string
|
||||
var bodyText string
|
||||
var subjectText string
|
||||
if result := <-cchan; result.Err != nil {
|
||||
l4g.Error("Failed to retrieve channel channel_id=%v, err=%v", post.ChannelId, result.Err)
|
||||
return
|
||||
} else {
|
||||
channel = result.Data.(*model.Channel)
|
||||
if channel.Type == model.CHANNEL_DIRECT {
|
||||
bodyText = "You have one new message."
|
||||
subjectText = "New Direct Message"
|
||||
} else {
|
||||
bodyText = "You have one new mention."
|
||||
subjectText = "New Mention"
|
||||
channelName = channel.DisplayName
|
||||
}
|
||||
}
|
||||
|
||||
var mentionedUsers []string
|
||||
|
||||
if result := <-uchan; result.Err != nil {
|
||||
l4g.Error("Failed to retrieve user profiles team_id=%v, err=%v", teamId, result.Err)
|
||||
return
|
||||
} else {
|
||||
profileMap := result.Data.(map[string]*model.User)
|
||||
|
||||
if _, ok := profileMap[post.UserId]; !ok {
|
||||
l4g.Error("Post user_id not returned by GetProfiles user_id=%v", post.UserId)
|
||||
return
|
||||
}
|
||||
senderName := profileMap[post.UserId].Username
|
||||
|
||||
toEmailMap := make(map[string]bool)
|
||||
|
||||
if channel.Type == model.CHANNEL_DIRECT {
|
||||
|
||||
var otherUserId string
|
||||
if userIds := strings.Split(channel.Name, "__"); userIds[0] == post.UserId {
|
||||
otherUserId = userIds[1]
|
||||
channelName = profileMap[userIds[1]].Username
|
||||
} else {
|
||||
otherUserId = userIds[0]
|
||||
channelName = profileMap[userIds[0]].Username
|
||||
}
|
||||
|
||||
otherUser := profileMap[otherUserId]
|
||||
sendEmail := true
|
||||
if _, ok := otherUser.NotifyProps["email"]; ok && otherUser.NotifyProps["email"] == "false" {
|
||||
sendEmail = false
|
||||
}
|
||||
if sendEmail && (otherUser.IsOffline() || otherUser.IsAway()) {
|
||||
toEmailMap[otherUserId] = true
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// Find out who is a member of the channel only keep those profiles
|
||||
if eResult := <-echan; eResult.Err != nil {
|
||||
l4g.Error("Failed to get channel members channel_id=%v err=%v", post.ChannelId, eResult.Err.Message)
|
||||
return
|
||||
} else {
|
||||
tempProfileMap := make(map[string]*model.User)
|
||||
members := eResult.Data.([]model.ChannelMember)
|
||||
for _, member := range members {
|
||||
tempProfileMap[member.UserId] = profileMap[member.UserId]
|
||||
}
|
||||
|
||||
profileMap = tempProfileMap
|
||||
}
|
||||
|
||||
// Build map for keywords
|
||||
keywordMap := make(map[string][]string)
|
||||
for _, profile := range profileMap {
|
||||
if len(profile.NotifyProps["mention_keys"]) > 0 {
|
||||
|
||||
// Add all the user's mention keys
|
||||
splitKeys := strings.Split(profile.NotifyProps["mention_keys"], ",")
|
||||
for _, k := range splitKeys {
|
||||
keywordMap[k] = append(keywordMap[strings.ToLower(k)], profile.Id)
|
||||
}
|
||||
|
||||
// If turned on, add the user's case sensitive first name
|
||||
if profile.NotifyProps["first_name"] == "true" {
|
||||
splitName := strings.Split(profile.FullName, " ")
|
||||
if len(splitName) > 0 && splitName[0] != "" {
|
||||
keywordMap[splitName[0]] = append(keywordMap[splitName[0]], profile.Id)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Build a map as a list of unique user_ids that are mentioned in this post
|
||||
splitF := func(c rune) bool {
|
||||
return c == ',' || c == ' ' || c == '.' || c == '!' || c == '?' || c == ':' || c == '<' || c == '>'
|
||||
}
|
||||
splitMessage := strings.FieldsFunc(strings.Replace(post.Message, "<br>", " ", -1), splitF)
|
||||
for _, word := range splitMessage {
|
||||
|
||||
// Non-case-sensitive check for regular keys
|
||||
userIds1, keyMatch := keywordMap[strings.ToLower(word)]
|
||||
|
||||
// Case-sensitive check for first name
|
||||
userIds2, firstNameMatch := keywordMap[word]
|
||||
|
||||
userIds := append(userIds1, userIds2...)
|
||||
|
||||
// If one of the non-case-senstive keys or the first name matches the word
|
||||
// then we add en entry to the sendEmail map
|
||||
if keyMatch || firstNameMatch {
|
||||
for _, userId := range userIds {
|
||||
if post.UserId == userId {
|
||||
continue
|
||||
}
|
||||
sendEmail := true
|
||||
if _, ok := profileMap[userId].NotifyProps["email"]; ok && profileMap[userId].NotifyProps["email"] == "false" {
|
||||
sendEmail = false
|
||||
}
|
||||
if sendEmail && (profileMap[userId].IsAway() || profileMap[userId].IsOffline()) {
|
||||
toEmailMap[userId] = true
|
||||
} else {
|
||||
toEmailMap[userId] = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for id, _ := range toEmailMap {
|
||||
fireAndForgetMentionUpdate(post.ChannelId, id)
|
||||
}
|
||||
}
|
||||
|
||||
if len(toEmailMap) != 0 {
|
||||
mentionedUsers = make([]string, 0, len(toEmailMap))
|
||||
for k := range toEmailMap {
|
||||
mentionedUsers = append(mentionedUsers, k)
|
||||
}
|
||||
|
||||
var teamName string
|
||||
if result := <-tchan; result.Err != nil {
|
||||
l4g.Error("Failed to retrieve team team_id=%v, err=%v", teamId, result.Err)
|
||||
return
|
||||
} else {
|
||||
teamName = result.Data.(*model.Team).Name
|
||||
}
|
||||
|
||||
// Build and send the emails
|
||||
location, _ := time.LoadLocation("UTC")
|
||||
tm := time.Unix(post.CreateAt/1000, 0).In(location)
|
||||
|
||||
subjectPage := NewServerTemplatePage("post_subject", teamUrl)
|
||||
subjectPage.Props["TeamName"] = teamName
|
||||
subjectPage.Props["SubjectText"] = subjectText
|
||||
subjectPage.Props["Month"] = tm.Month().String()[:3]
|
||||
subjectPage.Props["Day"] = fmt.Sprintf("%d", tm.Day())
|
||||
subjectPage.Props["Year"] = fmt.Sprintf("%d", tm.Year())
|
||||
|
||||
for id, doSend := range toEmailMap {
|
||||
|
||||
if !doSend {
|
||||
continue
|
||||
}
|
||||
|
||||
// skip if inactive
|
||||
if profileMap[id].DeleteAt > 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
firstName := strings.Split(profileMap[id].FullName, " ")[0]
|
||||
|
||||
bodyPage := NewServerTemplatePage("post_body", teamUrl)
|
||||
bodyPage.Props["FullName"] = firstName
|
||||
bodyPage.Props["TeamName"] = teamName
|
||||
bodyPage.Props["ChannelName"] = channelName
|
||||
bodyPage.Props["BodyText"] = bodyText
|
||||
bodyPage.Props["SenderName"] = senderName
|
||||
bodyPage.Props["Hour"] = fmt.Sprintf("%02d", tm.Hour())
|
||||
bodyPage.Props["Minute"] = fmt.Sprintf("%02d", tm.Minute())
|
||||
bodyPage.Props["Month"] = tm.Month().String()[:3]
|
||||
bodyPage.Props["Day"] = fmt.Sprintf("%d", tm.Day())
|
||||
bodyPage.Props["PostMessage"] = model.ClearMentionTags(post.Message)
|
||||
bodyPage.Props["TeamLink"] = teamUrl + "/channels/" + channel.Name
|
||||
|
||||
if err := utils.SendMail(profileMap[id].Email, subjectPage.Render(), bodyPage.Render()); err != nil {
|
||||
l4g.Error("Failed to send mention email successfully email=%v err=%v", profileMap[id].Email, err)
|
||||
}
|
||||
|
||||
if len(utils.Cfg.EmailSettings.ApplePushServer) > 0 {
|
||||
sessionChan := Srv.Store.Session().GetSessions(id)
|
||||
if result := <-sessionChan; result.Err != nil {
|
||||
l4g.Error("Failed to retrieve sessions in notifications id=%v, err=%v", id, result.Err)
|
||||
} else {
|
||||
sessions := result.Data.([]*model.Session)
|
||||
alreadySeen := make(map[string]string)
|
||||
|
||||
for _, session := range sessions {
|
||||
if len(session.DeviceId) > 0 && alreadySeen[session.DeviceId] == "" {
|
||||
|
||||
alreadySeen[session.DeviceId] = session.DeviceId
|
||||
|
||||
utils.FireAndForgetSendAppleNotify(session.DeviceId, subjectPage.Render(), 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
message := model.NewMessage(teamId, post.ChannelId, post.UserId, model.ACTION_POSTED)
|
||||
message.Add("post", post.ToJson())
|
||||
if len(mentionedUsers) != 0 {
|
||||
message.Add("mentions", model.ArrayToJson(mentionedUsers))
|
||||
}
|
||||
|
||||
store.PublishAndForget(message)
|
||||
}()
|
||||
}
|
||||
|
||||
func fireAndForgetMentionUpdate(channelId, userId string) {
|
||||
go func() {
|
||||
if result := <-Srv.Store.Channel().IncrementMentionCount(channelId, userId); result.Err != nil {
|
||||
l4g.Error("Failed to update mention count for user_id=%v on channel_id=%v err=%v", userId, channelId, result.Err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func updatePost(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
post := model.PostFromJson(r.Body)
|
||||
|
||||
if post == nil {
|
||||
c.SetInvalidParam("updatePost", "post")
|
||||
return
|
||||
}
|
||||
|
||||
cchan := Srv.Store.Channel().CheckPermissionsTo(c.Session.TeamId, post.ChannelId, c.Session.UserId)
|
||||
pchan := Srv.Store.Post().Get(post.Id)
|
||||
|
||||
if !c.HasPermissionsToChannel(cchan, "updatePost") {
|
||||
return
|
||||
}
|
||||
|
||||
var oldPost *model.Post
|
||||
if result := <-pchan; result.Err != nil {
|
||||
c.Err = result.Err
|
||||
return
|
||||
} else {
|
||||
oldPost = result.Data.(*model.PostList).Posts[post.Id]
|
||||
|
||||
if oldPost == nil {
|
||||
c.Err = model.NewAppError("updatePost", "We couldn't find the existing post or comment to update.", "id="+post.Id)
|
||||
c.Err.StatusCode = http.StatusBadRequest
|
||||
return
|
||||
}
|
||||
|
||||
if oldPost.UserId != c.Session.UserId {
|
||||
c.Err = model.NewAppError("updatePost", "You do not have the appropriate permissions", "oldUserId="+oldPost.UserId)
|
||||
c.Err.StatusCode = http.StatusForbidden
|
||||
return
|
||||
}
|
||||
|
||||
if oldPost.DeleteAt != 0 {
|
||||
c.Err = model.NewAppError("updatePost", "You do not have the appropriate permissions", "Already delted id="+post.Id)
|
||||
c.Err.StatusCode = http.StatusForbidden
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
hashtags, _ := model.ParseHashtags(post.Message)
|
||||
|
||||
if result := <-Srv.Store.Post().Update(oldPost, post.Message, hashtags); result.Err != nil {
|
||||
c.Err = result.Err
|
||||
return
|
||||
} else {
|
||||
rpost := result.Data.(*model.Post)
|
||||
|
||||
message := model.NewMessage(c.Session.TeamId, rpost.ChannelId, c.Session.UserId, model.ACTION_POST_EDITED)
|
||||
message.Add("post_id", rpost.Id)
|
||||
message.Add("channel_id", rpost.ChannelId)
|
||||
message.Add("message", rpost.Message)
|
||||
|
||||
store.PublishAndForget(message)
|
||||
|
||||
w.Write([]byte(rpost.ToJson()))
|
||||
}
|
||||
}
|
||||
|
||||
func getPosts(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
params := mux.Vars(r)
|
||||
|
||||
id := params["id"]
|
||||
if len(id) != 26 {
|
||||
c.SetInvalidParam("getPosts", "channelId")
|
||||
return
|
||||
}
|
||||
|
||||
offset, err := strconv.Atoi(params["offset"])
|
||||
if err != nil {
|
||||
c.SetInvalidParam("getPosts", "offset")
|
||||
return
|
||||
}
|
||||
|
||||
limit, err := strconv.Atoi(params["limit"])
|
||||
if err != nil {
|
||||
c.SetInvalidParam("getPosts", "limit")
|
||||
return
|
||||
}
|
||||
|
||||
cchan := Srv.Store.Channel().CheckPermissionsTo(c.Session.TeamId, id, c.Session.UserId)
|
||||
etagChan := Srv.Store.Post().GetEtag(id)
|
||||
|
||||
if !c.HasPermissionsToChannel(cchan, "getPosts") {
|
||||
return
|
||||
}
|
||||
|
||||
etag := (<-etagChan).Data.(string)
|
||||
|
||||
if HandleEtag(etag, w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
pchan := Srv.Store.Post().GetPosts(id, offset, limit)
|
||||
|
||||
if result := <-pchan; result.Err != nil {
|
||||
c.Err = result.Err
|
||||
return
|
||||
} else {
|
||||
list := result.Data.(*model.PostList)
|
||||
|
||||
w.Header().Set(model.HEADER_ETAG_SERVER, etag)
|
||||
w.Write([]byte(list.ToJson()))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func getPost(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
params := mux.Vars(r)
|
||||
|
||||
channelId := params["id"]
|
||||
if len(channelId) != 26 {
|
||||
c.SetInvalidParam("getPost", "channelId")
|
||||
return
|
||||
}
|
||||
|
||||
postId := params["post_id"]
|
||||
if len(postId) != 26 {
|
||||
c.SetInvalidParam("getPost", "postId")
|
||||
return
|
||||
}
|
||||
|
||||
cchan := Srv.Store.Channel().CheckPermissionsTo(c.Session.TeamId, channelId, c.Session.UserId)
|
||||
pchan := Srv.Store.Post().Get(postId)
|
||||
|
||||
if !c.HasPermissionsToChannel(cchan, "getPost") {
|
||||
return
|
||||
}
|
||||
|
||||
if result := <-pchan; result.Err != nil {
|
||||
c.Err = result.Err
|
||||
return
|
||||
} else if HandleEtag(result.Data.(*model.PostList).Etag(), w, r) {
|
||||
return
|
||||
} else {
|
||||
list := result.Data.(*model.PostList)
|
||||
|
||||
if !list.IsChannelId(channelId) {
|
||||
c.Err = model.NewAppError("getPost", "You do not have the appropriate permissions", "")
|
||||
c.Err.StatusCode = http.StatusForbidden
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set(model.HEADER_ETAG_SERVER, list.Etag())
|
||||
w.Write([]byte(list.ToJson()))
|
||||
}
|
||||
}
|
||||
|
||||
func deletePost(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
params := mux.Vars(r)
|
||||
|
||||
channelId := params["id"]
|
||||
if len(channelId) != 26 {
|
||||
c.SetInvalidParam("deletePost", "channelId")
|
||||
return
|
||||
}
|
||||
|
||||
postId := params["post_id"]
|
||||
if len(postId) != 26 {
|
||||
c.SetInvalidParam("deletePost", "postId")
|
||||
return
|
||||
}
|
||||
|
||||
cchan := Srv.Store.Channel().CheckPermissionsTo(c.Session.TeamId, channelId, c.Session.UserId)
|
||||
pchan := Srv.Store.Post().Get(postId)
|
||||
|
||||
if !c.HasPermissionsToChannel(cchan, "deletePost") {
|
||||
return
|
||||
}
|
||||
|
||||
if result := <-pchan; result.Err != nil {
|
||||
c.Err = result.Err
|
||||
return
|
||||
} else {
|
||||
post := result.Data.(*model.PostList).Posts[postId]
|
||||
|
||||
if post == nil {
|
||||
c.SetInvalidParam("deletePost", "postId")
|
||||
return
|
||||
}
|
||||
|
||||
if post.ChannelId != channelId {
|
||||
c.Err = model.NewAppError("deletePost", "You do not have the appropriate permissions", "")
|
||||
c.Err.StatusCode = http.StatusForbidden
|
||||
return
|
||||
}
|
||||
|
||||
if post.UserId != c.Session.UserId {
|
||||
c.Err = model.NewAppError("deletePost", "You do not have the appropriate permissions", "")
|
||||
c.Err.StatusCode = http.StatusForbidden
|
||||
return
|
||||
}
|
||||
|
||||
if dresult := <-Srv.Store.Post().Delete(postId, model.GetMillis()); dresult.Err != nil {
|
||||
c.Err = dresult.Err
|
||||
return
|
||||
}
|
||||
|
||||
message := model.NewMessage(c.Session.TeamId, post.ChannelId, c.Session.UserId, model.ACTION_POST_DELETED)
|
||||
message.Add("post_id", post.Id)
|
||||
message.Add("channel_id", post.ChannelId)
|
||||
|
||||
store.PublishAndForget(message)
|
||||
|
||||
result := make(map[string]string)
|
||||
result["id"] = postId
|
||||
w.Write([]byte(model.MapToJson(result)))
|
||||
}
|
||||
}
|
||||
|
||||
func searchPosts(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
terms := r.FormValue("terms")
|
||||
|
||||
if len(terms) == 0 {
|
||||
c.SetInvalidParam("search", "terms")
|
||||
return
|
||||
}
|
||||
|
||||
hashtagTerms, plainTerms := model.ParseHashtags(terms)
|
||||
|
||||
var hchan store.StoreChannel
|
||||
if len(hashtagTerms) != 0 {
|
||||
hchan = Srv.Store.Post().Search(c.Session.TeamId, c.Session.UserId, hashtagTerms, true)
|
||||
}
|
||||
|
||||
var pchan store.StoreChannel
|
||||
if len(plainTerms) != 0 {
|
||||
pchan = Srv.Store.Post().Search(c.Session.TeamId, c.Session.UserId, terms, false)
|
||||
}
|
||||
|
||||
mainList := &model.PostList{}
|
||||
if hchan != nil {
|
||||
if result := <-hchan; result.Err != nil {
|
||||
c.Err = result.Err
|
||||
return
|
||||
} else {
|
||||
mainList = result.Data.(*model.PostList)
|
||||
}
|
||||
}
|
||||
|
||||
plainList := &model.PostList{}
|
||||
if pchan != nil {
|
||||
if result := <-pchan; result.Err != nil {
|
||||
c.Err = result.Err
|
||||
return
|
||||
} else {
|
||||
plainList = result.Data.(*model.PostList)
|
||||
}
|
||||
}
|
||||
|
||||
for _, postId := range plainList.Order {
|
||||
if _, ok := mainList.Posts[postId]; !ok {
|
||||
mainList.AddPost(plainList.Posts[postId])
|
||||
mainList.AddOrder(postId)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
w.Write([]byte(mainList.ToJson()))
|
||||
}
|
||||
130
api/post_benchmark_test.go
Обычный файл
130
api/post_benchmark_test.go
Обычный файл
@@ -0,0 +1,130 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/mattermost/platform/utils"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const (
|
||||
NUM_POSTS = 100
|
||||
)
|
||||
|
||||
func BenchmarkCreatePost(b *testing.B) {
|
||||
var (
|
||||
NUM_POSTS_RANGE = utils.Range{NUM_POSTS, NUM_POSTS}
|
||||
)
|
||||
_, _, channel := SetupBenchmark()
|
||||
|
||||
testPoster := NewAutoPostCreator(Client, channel.Id)
|
||||
|
||||
// Benchmark Start
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
testPoster.CreateTestPosts(NUM_POSTS_RANGE)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkUpdatePost(b *testing.B) {
|
||||
var (
|
||||
NUM_POSTS_RANGE = utils.Range{NUM_POSTS, NUM_POSTS}
|
||||
UPDATE_POST_LEN = 100
|
||||
)
|
||||
_, _, channel := SetupBenchmark()
|
||||
|
||||
testPoster := NewAutoPostCreator(Client, channel.Id)
|
||||
posts, valid := testPoster.CreateTestPosts(NUM_POSTS_RANGE)
|
||||
if valid == false {
|
||||
b.Fatal("Unable to create test posts")
|
||||
}
|
||||
|
||||
for i := range posts {
|
||||
posts[i].Message = utils.RandString(UPDATE_POST_LEN, utils.ALPHANUMERIC)
|
||||
}
|
||||
|
||||
// Benchmark Start
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
for i := range posts {
|
||||
if _, err := Client.UpdatePost(posts[i]); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkGetPosts(b *testing.B) {
|
||||
var (
|
||||
NUM_POSTS_RANGE = utils.Range{NUM_POSTS, NUM_POSTS}
|
||||
)
|
||||
_, _, channel := SetupBenchmark()
|
||||
|
||||
testPoster := NewAutoPostCreator(Client, channel.Id)
|
||||
testPoster.CreateTestPosts(NUM_POSTS_RANGE)
|
||||
|
||||
// Benchmark Start
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
Client.Must(Client.GetPosts(channel.Id, 0, NUM_POSTS, ""))
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSearchPosts(b *testing.B) {
|
||||
var (
|
||||
NUM_POSTS_RANGE = utils.Range{NUM_POSTS, NUM_POSTS}
|
||||
)
|
||||
_, _, channel := SetupBenchmark()
|
||||
|
||||
testPoster := NewAutoPostCreator(Client, channel.Id)
|
||||
testPoster.CreateTestPosts(NUM_POSTS_RANGE)
|
||||
|
||||
// Benchmark Start
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
Client.Must(Client.SearchPosts("nothere"))
|
||||
Client.Must(Client.SearchPosts("n"))
|
||||
Client.Must(Client.SearchPosts("#tag"))
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkEtagCache(b *testing.B) {
|
||||
var (
|
||||
NUM_POSTS_RANGE = utils.Range{NUM_POSTS, NUM_POSTS}
|
||||
)
|
||||
_, _, channel := SetupBenchmark()
|
||||
|
||||
testPoster := NewAutoPostCreator(Client, channel.Id)
|
||||
testPoster.CreateTestPosts(NUM_POSTS_RANGE)
|
||||
|
||||
etag := Client.Must(Client.GetPosts(channel.Id, 0, NUM_POSTS/2, "")).Etag
|
||||
|
||||
// Benchmark Start
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
Client.Must(Client.GetPosts(channel.Id, 0, NUM_POSTS/2, etag))
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkDeletePosts(b *testing.B) {
|
||||
var (
|
||||
NUM_POSTS_RANGE = utils.Range{NUM_POSTS, NUM_POSTS}
|
||||
)
|
||||
_, _, channel := SetupBenchmark()
|
||||
|
||||
testPoster := NewAutoPostCreator(Client, channel.Id)
|
||||
posts, valid := testPoster.CreateTestPosts(NUM_POSTS_RANGE)
|
||||
if valid == false {
|
||||
b.Fatal("Unable to create test posts")
|
||||
}
|
||||
|
||||
// Benchmark Start
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
for i := range posts {
|
||||
Client.Must(Client.DeletePost(channel.Id, posts[i].Id))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
566
api/post_test.go
Обычный файл
566
api/post_test.go
Обычный файл
@@ -0,0 +1,566 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/mattermost/platform/model"
|
||||
"github.com/mattermost/platform/utils"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestCreatePost(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
|
||||
team2 := &model.Team{Name: "Name Team 2", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team2 = Client.Must(Client.CreateTeam(team2)).Data.(*model.Team)
|
||||
|
||||
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(user1.Id)
|
||||
|
||||
user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(user2.Id)
|
||||
|
||||
Client.LoginByEmail(team.Domain, user1.Email, "pwd")
|
||||
|
||||
channel1 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
|
||||
|
||||
channel2 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channel2 = Client.Must(Client.CreateChannel(channel2)).Data.(*model.Channel)
|
||||
|
||||
filenames := []string{"/api/v1/files/get/12345678901234567890123456/12345678901234567890123456/test.png", "/api/v1/files/get/" + channel1.Id + "/" + user1.Id + "/test.png", "www.mattermost.com/fake/url", "junk"}
|
||||
|
||||
post1 := &model.Post{ChannelId: channel1.Id, Message: "#hashtag a" + model.NewId() + "a", Filenames: filenames}
|
||||
rpost1, err := Client.CreatePost(post1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if rpost1.Data.(*model.Post).Message != post1.Message {
|
||||
t.Fatal("message didn't match")
|
||||
}
|
||||
|
||||
if rpost1.Data.(*model.Post).Hashtags != "#hashtag" {
|
||||
t.Fatal("hashtag didn't match")
|
||||
}
|
||||
|
||||
if len(rpost1.Data.(*model.Post).Filenames) != 2 {
|
||||
t.Fatal("filenames didn't parse correctly")
|
||||
}
|
||||
|
||||
post2 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a", RootId: rpost1.Data.(*model.Post).Id}
|
||||
rpost2, err := Client.CreatePost(post2)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
post3 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a", RootId: rpost1.Data.(*model.Post).Id, ParentId: rpost2.Data.(*model.Post).Id}
|
||||
_, err = Client.CreatePost(post3)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
post4 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a", RootId: "junk"}
|
||||
_, err = Client.CreatePost(post4)
|
||||
if err.StatusCode != http.StatusBadRequest {
|
||||
t.Fatal("Should have been invalid param")
|
||||
}
|
||||
|
||||
post5 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a", RootId: rpost1.Data.(*model.Post).Id, ParentId: "junk"}
|
||||
_, err = Client.CreatePost(post5)
|
||||
if err.StatusCode != http.StatusBadRequest {
|
||||
t.Fatal("Should have been invalid param")
|
||||
}
|
||||
|
||||
post1c2 := &model.Post{ChannelId: channel2.Id, Message: "a" + model.NewId() + "a"}
|
||||
rpost1c2, err := Client.CreatePost(post1c2)
|
||||
|
||||
post2c2 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a", RootId: rpost1c2.Data.(*model.Post).Id}
|
||||
_, err = Client.CreatePost(post2c2)
|
||||
if err.StatusCode != http.StatusBadRequest {
|
||||
t.Fatal("Should have been invalid param")
|
||||
}
|
||||
|
||||
post6 := &model.Post{ChannelId: "junk", Message: "a" + model.NewId() + "a"}
|
||||
_, err = Client.CreatePost(post6)
|
||||
if err.StatusCode != http.StatusForbidden {
|
||||
t.Fatal("Should have been forbidden")
|
||||
}
|
||||
|
||||
Client.LoginByEmail(team.Domain, user2.Email, "pwd")
|
||||
post7 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"}
|
||||
_, err = Client.CreatePost(post7)
|
||||
if err.StatusCode != http.StatusForbidden {
|
||||
t.Fatal("Should have been forbidden")
|
||||
}
|
||||
|
||||
user3 := &model.User{TeamId: team2.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
user3 = Client.Must(Client.CreateUser(user3, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(user3.Id)
|
||||
|
||||
Client.LoginByEmail(team2.Domain, user3.Email, "pwd")
|
||||
|
||||
channel3 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team2.Id}
|
||||
channel3 = Client.Must(Client.CreateChannel(channel3)).Data.(*model.Channel)
|
||||
|
||||
post8 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"}
|
||||
_, err = Client.CreatePost(post8)
|
||||
if err.StatusCode != http.StatusForbidden {
|
||||
t.Fatal("Should have been forbidden")
|
||||
}
|
||||
|
||||
if _, err = Client.DoPost("/channels/"+channel3.Id+"/create", "garbage"); err == nil {
|
||||
t.Fatal("should have been an error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateValetPost(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
|
||||
team2 := &model.Team{Name: "Name Team 2", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team2 = Client.Must(Client.CreateTeam(team2)).Data.(*model.Team)
|
||||
|
||||
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(user1.Id)
|
||||
|
||||
user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(user2.Id)
|
||||
|
||||
Client.LoginByEmail(team.Domain, user1.Email, "pwd")
|
||||
|
||||
channel1 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
|
||||
|
||||
channel2 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channel2 = Client.Must(Client.CreateChannel(channel2)).Data.(*model.Channel)
|
||||
|
||||
post1 := &model.Post{ChannelId: channel1.Id, Message: "#hashtag a" + model.NewId() + "a"}
|
||||
rpost1, err := Client.CreateValetPost(post1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if rpost1.Data.(*model.Post).Message != post1.Message {
|
||||
t.Fatal("message didn't match")
|
||||
}
|
||||
|
||||
if rpost1.Data.(*model.Post).Hashtags != "#hashtag" {
|
||||
t.Fatal("hashtag didn't match")
|
||||
}
|
||||
|
||||
post2 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a", RootId: rpost1.Data.(*model.Post).Id}
|
||||
rpost2, err := Client.CreateValetPost(post2)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
post3 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a", RootId: rpost1.Data.(*model.Post).Id, ParentId: rpost2.Data.(*model.Post).Id}
|
||||
_, err = Client.CreateValetPost(post3)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
post4 := &model.Post{ChannelId: "junk", Message: "a" + model.NewId() + "a"}
|
||||
_, err = Client.CreateValetPost(post4)
|
||||
if err.StatusCode != http.StatusForbidden {
|
||||
t.Fatal("Should have been forbidden")
|
||||
}
|
||||
|
||||
Client.LoginByEmail(team.Domain, user2.Email, "pwd")
|
||||
post5 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"}
|
||||
_, err = Client.CreateValetPost(post5)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
user3 := &model.User{TeamId: team2.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
user3 = Client.Must(Client.CreateUser(user3, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(user3.Id)
|
||||
|
||||
Client.LoginByEmail(team2.Domain, user3.Email, "pwd")
|
||||
|
||||
channel3 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team2.Id}
|
||||
channel3 = Client.Must(Client.CreateChannel(channel3)).Data.(*model.Channel)
|
||||
|
||||
post6 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"}
|
||||
_, err = Client.CreateValetPost(post6)
|
||||
if err.StatusCode != http.StatusForbidden {
|
||||
t.Fatal("Should have been forbidden")
|
||||
}
|
||||
|
||||
if _, err = Client.DoPost("/channels/"+channel3.Id+"/create", "garbage"); err == nil {
|
||||
t.Fatal("should have been an error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdatePost(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
|
||||
team2 := &model.Team{Name: "Name Team 2", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team2 = Client.Must(Client.CreateTeam(team2)).Data.(*model.Team)
|
||||
|
||||
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(user1.Id)
|
||||
|
||||
user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(user2.Id)
|
||||
|
||||
Client.LoginByEmail(team.Domain, user1.Email, "pwd")
|
||||
|
||||
channel1 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
|
||||
|
||||
channel2 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channel2 = Client.Must(Client.CreateChannel(channel2)).Data.(*model.Channel)
|
||||
|
||||
post1 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"}
|
||||
rpost1, err := Client.CreatePost(post1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if rpost1.Data.(*model.Post).Message != post1.Message {
|
||||
t.Fatal("full name didn't match")
|
||||
}
|
||||
|
||||
post2 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a", RootId: rpost1.Data.(*model.Post).Id}
|
||||
rpost2, err := Client.CreatePost(post2)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
msg2 := "a" + model.NewId() + " update post 1"
|
||||
rpost2.Data.(*model.Post).Message = msg2
|
||||
if rupost2, err := Client.UpdatePost(rpost2.Data.(*model.Post)); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
if rupost2.Data.(*model.Post).Message != msg2 {
|
||||
t.Fatal("failed to updates")
|
||||
}
|
||||
}
|
||||
|
||||
msg1 := "#hashtag a" + model.NewId() + " update post 2"
|
||||
rpost1.Data.(*model.Post).Message = msg1
|
||||
if rupost1, err := Client.UpdatePost(rpost1.Data.(*model.Post)); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
if rupost1.Data.(*model.Post).Message != msg1 && rupost1.Data.(*model.Post).Hashtags != "#hashtag" {
|
||||
t.Fatal("failed to updates")
|
||||
}
|
||||
}
|
||||
|
||||
up12 := &model.Post{Id: rpost1.Data.(*model.Post).Id, ChannelId: channel1.Id, Message: "a" + model.NewId() + " updaet post 1 update 2"}
|
||||
if rup12, err := Client.UpdatePost(up12); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
if rup12.Data.(*model.Post).Message != up12.Message {
|
||||
t.Fatal("failed to updates")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetPosts(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
|
||||
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(user1.Id)
|
||||
|
||||
Client.LoginByEmail(team.Domain, user1.Email, "pwd")
|
||||
|
||||
channel1 := &model.Channel{DisplayName: "TestGetPosts", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
|
||||
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
post1 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"}
|
||||
post1 = Client.Must(Client.CreatePost(post1)).Data.(*model.Post)
|
||||
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
post1a1 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a", RootId: post1.Id}
|
||||
post1a1 = Client.Must(Client.CreatePost(post1a1)).Data.(*model.Post)
|
||||
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
post2 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"}
|
||||
post2 = Client.Must(Client.CreatePost(post2)).Data.(*model.Post)
|
||||
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
post3 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"}
|
||||
post3 = Client.Must(Client.CreatePost(post3)).Data.(*model.Post)
|
||||
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
post3a1 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a", RootId: post3.Id}
|
||||
post3a1 = Client.Must(Client.CreatePost(post3a1)).Data.(*model.Post)
|
||||
|
||||
r1 := Client.Must(Client.GetPosts(channel1.Id, 0, 2, "")).Data.(*model.PostList)
|
||||
|
||||
if r1.Order[0] != post3a1.Id {
|
||||
t.Fatal("wrong order")
|
||||
}
|
||||
|
||||
if r1.Order[1] != post3.Id {
|
||||
t.Fatal("wrong order")
|
||||
}
|
||||
|
||||
if len(r1.Posts) != 4 {
|
||||
t.Fatal("wrong size")
|
||||
}
|
||||
|
||||
r2 := Client.Must(Client.GetPosts(channel1.Id, 2, 2, "")).Data.(*model.PostList)
|
||||
|
||||
if r2.Order[0] != post2.Id {
|
||||
t.Fatal("wrong order")
|
||||
}
|
||||
|
||||
if r2.Order[1] != post1a1.Id {
|
||||
t.Fatal("wrong order")
|
||||
}
|
||||
|
||||
if len(r2.Posts) != 4 {
|
||||
t.Log(r2.Posts)
|
||||
t.Fatal("wrong size")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSearchPosts(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
|
||||
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(user1.Id)
|
||||
|
||||
Client.LoginByEmail(team.Domain, user1.Email, "pwd")
|
||||
|
||||
channel1 := &model.Channel{DisplayName: "TestGetPosts", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
|
||||
|
||||
post1 := &model.Post{ChannelId: channel1.Id, Message: "search for post1"}
|
||||
post1 = Client.Must(Client.CreatePost(post1)).Data.(*model.Post)
|
||||
|
||||
post2 := &model.Post{ChannelId: channel1.Id, Message: "search for post2"}
|
||||
post2 = Client.Must(Client.CreatePost(post2)).Data.(*model.Post)
|
||||
|
||||
post3 := &model.Post{ChannelId: channel1.Id, Message: "#hashtag search for post3"}
|
||||
post3 = Client.Must(Client.CreatePost(post3)).Data.(*model.Post)
|
||||
|
||||
post4 := &model.Post{ChannelId: channel1.Id, Message: "hashtag for post4"}
|
||||
post4 = Client.Must(Client.CreatePost(post4)).Data.(*model.Post)
|
||||
|
||||
r1 := Client.Must(Client.SearchPosts("search")).Data.(*model.PostList)
|
||||
|
||||
if len(r1.Order) != 3 {
|
||||
t.Fatal("wrong serach")
|
||||
}
|
||||
|
||||
r2 := Client.Must(Client.SearchPosts("post2")).Data.(*model.PostList)
|
||||
|
||||
if len(r2.Order) != 1 && r2.Order[0] == post2.Id {
|
||||
t.Fatal("wrong serach")
|
||||
}
|
||||
|
||||
r3 := Client.Must(Client.SearchPosts("#hashtag")).Data.(*model.PostList)
|
||||
|
||||
if len(r3.Order) != 1 && r3.Order[0] == post3.Id {
|
||||
t.Fatal("wrong serach")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSearchHashtagPosts(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
|
||||
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(user1.Id)
|
||||
|
||||
Client.LoginByEmail(team.Domain, user1.Email, "pwd")
|
||||
|
||||
channel1 := &model.Channel{DisplayName: "TestGetPosts", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
|
||||
|
||||
post1 := &model.Post{ChannelId: channel1.Id, Message: "#sgtitlereview with space"}
|
||||
post1 = Client.Must(Client.CreatePost(post1)).Data.(*model.Post)
|
||||
|
||||
post2 := &model.Post{ChannelId: channel1.Id, Message: "#sgtitlereview\n with return"}
|
||||
post2 = Client.Must(Client.CreatePost(post2)).Data.(*model.Post)
|
||||
|
||||
post3 := &model.Post{ChannelId: channel1.Id, Message: "no hashtag"}
|
||||
post3 = Client.Must(Client.CreatePost(post3)).Data.(*model.Post)
|
||||
|
||||
r1 := Client.Must(Client.SearchPosts("#sgtitlereview")).Data.(*model.PostList)
|
||||
|
||||
if len(r1.Order) != 2 {
|
||||
t.Fatal("wrong search")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetPostsCache(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
|
||||
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(user1.Id)
|
||||
|
||||
Client.LoginByEmail(team.Domain, user1.Email, "pwd")
|
||||
|
||||
channel1 := &model.Channel{DisplayName: "TestGetPosts", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
|
||||
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
post1 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"}
|
||||
post1 = Client.Must(Client.CreatePost(post1)).Data.(*model.Post)
|
||||
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
post2 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"}
|
||||
post2 = Client.Must(Client.CreatePost(post2)).Data.(*model.Post)
|
||||
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
post3 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"}
|
||||
post3 = Client.Must(Client.CreatePost(post3)).Data.(*model.Post)
|
||||
|
||||
etag := Client.Must(Client.GetPosts(channel1.Id, 0, 2, "")).Etag
|
||||
|
||||
// test etag caching
|
||||
if cache_result, err := Client.GetPosts(channel1.Id, 0, 2, etag); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if cache_result.Data.(*model.PostList) != nil {
|
||||
t.Log(cache_result.Data)
|
||||
t.Fatal("cache should be empty")
|
||||
}
|
||||
|
||||
etag = Client.Must(Client.GetPost(channel1.Id, post1.Id, "")).Etag
|
||||
|
||||
// test etag caching
|
||||
if cache_result, err := Client.GetPost(channel1.Id, post1.Id, etag); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if cache_result.Data.(*model.PostList) != nil {
|
||||
t.Log(cache_result.Data)
|
||||
t.Fatal("cache should be empty")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestDeletePosts(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
|
||||
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(user1.Id)
|
||||
|
||||
Client.LoginByEmail(team.Domain, user1.Email, "pwd")
|
||||
|
||||
channel1 := &model.Channel{DisplayName: "TestGetPosts", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
|
||||
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
post1 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"}
|
||||
post1 = Client.Must(Client.CreatePost(post1)).Data.(*model.Post)
|
||||
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
post1a1 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a", RootId: post1.Id}
|
||||
post1a1 = Client.Must(Client.CreatePost(post1a1)).Data.(*model.Post)
|
||||
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
post1a2 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a", RootId: post1.Id, ParentId: post1a1.Id}
|
||||
post1a2 = Client.Must(Client.CreatePost(post1a2)).Data.(*model.Post)
|
||||
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
post2 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"}
|
||||
post2 = Client.Must(Client.CreatePost(post2)).Data.(*model.Post)
|
||||
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
post3 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"}
|
||||
post3 = Client.Must(Client.CreatePost(post3)).Data.(*model.Post)
|
||||
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
post3a1 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a", RootId: post3.Id}
|
||||
post3a1 = Client.Must(Client.CreatePost(post3a1)).Data.(*model.Post)
|
||||
|
||||
Client.Must(Client.DeletePost(channel1.Id, post3.Id))
|
||||
|
||||
r2 := Client.Must(Client.GetPosts(channel1.Id, 0, 10, "")).Data.(*model.PostList)
|
||||
|
||||
if len(r2.Posts) != 4 {
|
||||
t.Fatal("should have returned 5 items")
|
||||
}
|
||||
}
|
||||
|
||||
func TestEmailMention(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
|
||||
user1 := &model.User{TeamId: team.Id, Email: "corey@test.com", FullName: "Bob Bobby", Password: "pwd"}
|
||||
user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(user1.Id)
|
||||
|
||||
Client.LoginByEmail(team.Domain, user1.Email, "pwd")
|
||||
|
||||
channel1 := &model.Channel{DisplayName: "TestGetPosts", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
|
||||
|
||||
post1 := &model.Post{ChannelId: channel1.Id, Message: "bob"}
|
||||
post1 = Client.Must(Client.CreatePost(post1)).Data.(*model.Post)
|
||||
|
||||
// No easy way to verify the email was sent, but this will at least cause the server to throw errors if the code is broken
|
||||
|
||||
}
|
||||
|
||||
func TestFuzzyPosts(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
|
||||
user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(user.Id)
|
||||
|
||||
Client.LoginByEmail(team.Domain, user.Email, "pwd")
|
||||
|
||||
channel1 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
|
||||
|
||||
filenames := []string{"junk"}
|
||||
|
||||
for i := 0; i < len(utils.FUZZY_STRINGS_POSTS); i++ {
|
||||
post := &model.Post{ChannelId: channel1.Id, Message: utils.FUZZY_STRINGS_POSTS[i], Filenames: filenames}
|
||||
|
||||
_, err := Client.CreatePost(post)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
60
api/server.go
Обычный файл
60
api/server.go
Обычный файл
@@ -0,0 +1,60 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
l4g "code.google.com/p/log4go"
|
||||
"github.com/braintree/manners"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/mattermost/platform/store"
|
||||
"github.com/mattermost/platform/utils"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
Server *manners.GracefulServer
|
||||
Store store.Store
|
||||
Router *mux.Router
|
||||
}
|
||||
|
||||
var Srv *Server
|
||||
|
||||
func NewServer() {
|
||||
|
||||
l4g.Info("Server is initializing...")
|
||||
|
||||
Srv = &Server{}
|
||||
Srv.Server = manners.NewServer()
|
||||
Srv.Store = store.NewSqlStore()
|
||||
store.RedisClient()
|
||||
|
||||
Srv.Router = mux.NewRouter()
|
||||
Srv.Router.NotFoundHandler = http.HandlerFunc(Handle404)
|
||||
}
|
||||
|
||||
func StartServer() {
|
||||
l4g.Info("Starting Server...")
|
||||
|
||||
l4g.Info("Server is listening on " + utils.Cfg.ServiceSettings.Port)
|
||||
go func() {
|
||||
err := Srv.Server.ListenAndServe(":"+utils.Cfg.ServiceSettings.Port, Srv.Router)
|
||||
if err != nil {
|
||||
l4g.Critical("Error starting server, err:%v", err)
|
||||
time.Sleep(time.Second)
|
||||
panic("Error starting server " + err.Error())
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func StopServer() {
|
||||
|
||||
l4g.Info("Stopping Server...")
|
||||
|
||||
Srv.Server.Shutdown <- true
|
||||
Srv.Store.Close()
|
||||
store.RedisClose()
|
||||
|
||||
l4g.Info("Server stopped")
|
||||
}
|
||||
11
api/server_test.go
Обычный файл
11
api/server_test.go
Обычный файл
@@ -0,0 +1,11 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestServer(t *testing.T) {
|
||||
}
|
||||
542
api/team.go
Обычный файл
542
api/team.go
Обычный файл
@@ -0,0 +1,542 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
l4g "code.google.com/p/log4go"
|
||||
"fmt"
|
||||
"github.com/awslabs/aws-sdk-go/aws"
|
||||
"github.com/awslabs/aws-sdk-go/service/route53"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/mattermost/platform/model"
|
||||
"github.com/mattermost/platform/utils"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func InitTeam(r *mux.Router) {
|
||||
l4g.Debug("Initializing team api routes")
|
||||
|
||||
sr := r.PathPrefix("/teams").Subrouter()
|
||||
sr.Handle("/create", ApiAppHandler(createTeam)).Methods("POST")
|
||||
sr.Handle("/create_from_signup", ApiAppHandler(createTeamFromSignup)).Methods("POST")
|
||||
sr.Handle("/signup", ApiAppHandler(signupTeam)).Methods("POST")
|
||||
sr.Handle("/find_team_by_domain", ApiAppHandler(findTeamByDomain)).Methods("POST")
|
||||
sr.Handle("/find_teams", ApiAppHandler(findTeams)).Methods("POST")
|
||||
sr.Handle("/email_teams", ApiAppHandler(emailTeams)).Methods("POST")
|
||||
sr.Handle("/invite_members", ApiUserRequired(inviteMembers)).Methods("POST")
|
||||
sr.Handle("/update_name", ApiUserRequired(updateTeamName)).Methods("POST")
|
||||
}
|
||||
|
||||
func signupTeam(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
m := model.MapFromJson(r.Body)
|
||||
email := strings.ToLower(strings.TrimSpace(m["email"]))
|
||||
name := strings.TrimSpace(m["name"])
|
||||
|
||||
if len(email) == 0 {
|
||||
c.SetInvalidParam("signupTeam", "email")
|
||||
return
|
||||
}
|
||||
|
||||
if len(name) == 0 {
|
||||
c.SetInvalidParam("signupTeam", "name")
|
||||
return
|
||||
}
|
||||
|
||||
subjectPage := NewServerTemplatePage("signup_team_subject", c.TeamUrl)
|
||||
bodyPage := NewServerTemplatePage("signup_team_body", c.TeamUrl)
|
||||
bodyPage.Props["TourUrl"] = utils.Cfg.TeamSettings.TourLink
|
||||
|
||||
props := make(map[string]string)
|
||||
props["email"] = email
|
||||
props["name"] = name
|
||||
props["time"] = fmt.Sprintf("%v", model.GetMillis())
|
||||
|
||||
data := model.MapToJson(props)
|
||||
hash := model.HashPassword(fmt.Sprintf("%v:%v", data, utils.Cfg.ServiceSettings.InviteSalt))
|
||||
|
||||
bodyPage.Props["Link"] = fmt.Sprintf("%s/signup_team_complete/?d=%s&h=%s", c.TeamUrl, url.QueryEscape(data), url.QueryEscape(hash))
|
||||
|
||||
if err := utils.SendMail(email, subjectPage.Render(), bodyPage.Render()); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
if utils.Cfg.ServiceSettings.Mode == utils.MODE_DEV {
|
||||
m["follow_link"] = bodyPage.Props["Link"]
|
||||
}
|
||||
|
||||
w.Header().Set("Access-Control-Allow-Origin", " *")
|
||||
w.Write([]byte(model.MapToJson(m)))
|
||||
}
|
||||
|
||||
func createTeamFromSignup(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
teamSignup := model.TeamSignupFromJson(r.Body)
|
||||
|
||||
if teamSignup == nil {
|
||||
c.SetInvalidParam("createTeam", "teamSignup")
|
||||
return
|
||||
}
|
||||
|
||||
props := model.MapFromJson(strings.NewReader(teamSignup.Data))
|
||||
teamSignup.Team.Email = props["email"]
|
||||
teamSignup.User.Email = props["email"]
|
||||
|
||||
teamSignup.Team.PreSave()
|
||||
|
||||
if err := teamSignup.Team.IsValid(); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
teamSignup.Team.Id = ""
|
||||
|
||||
password := teamSignup.User.Password
|
||||
teamSignup.User.PreSave()
|
||||
teamSignup.User.TeamId = model.NewId()
|
||||
if err := teamSignup.User.IsValid(); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
teamSignup.User.Id = ""
|
||||
teamSignup.User.TeamId = ""
|
||||
teamSignup.User.Password = password
|
||||
|
||||
if !model.ComparePassword(teamSignup.Hash, fmt.Sprintf("%v:%v", teamSignup.Data, utils.Cfg.ServiceSettings.InviteSalt)) {
|
||||
c.Err = model.NewAppError("createTeamFromSignup", "The signup link does not appear to be valid", "")
|
||||
return
|
||||
}
|
||||
|
||||
t, err := strconv.ParseInt(props["time"], 10, 64)
|
||||
if err != nil || model.GetMillis()-t > 1000*60*60 { // one hour
|
||||
c.Err = model.NewAppError("createTeamFromSignup", "The signup link has expired", "")
|
||||
return
|
||||
}
|
||||
|
||||
found := FindTeamByDomain(c, teamSignup.Team.Domain, "true")
|
||||
if c.Err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if found {
|
||||
c.Err = model.NewAppError("createTeamFromSignup", "This URL is unavailable. Please try another.", "d="+teamSignup.Team.Domain)
|
||||
return
|
||||
}
|
||||
|
||||
if IsBetaDomain(r) {
|
||||
for key, value := range utils.Cfg.ServiceSettings.Shards {
|
||||
if strings.Index(r.Host, key) == 0 {
|
||||
createSubDomain(teamSignup.Team.Domain, value)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if result := <-Srv.Store.Team().Save(&teamSignup.Team); result.Err != nil {
|
||||
c.Err = result.Err
|
||||
return
|
||||
} else {
|
||||
rteam := result.Data.(*model.Team)
|
||||
|
||||
channel := &model.Channel{DisplayName: "Town Square", Name: "town-square", Type: model.CHANNEL_OPEN, TeamId: rteam.Id}
|
||||
|
||||
if _, err := CreateChannel(c, channel, r.URL.Path, false); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
teamSignup.User.TeamId = rteam.Id
|
||||
teamSignup.User.EmailVerified = true
|
||||
|
||||
ruser := CreateUser(c, rteam, &teamSignup.User)
|
||||
if c.Err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
CreateValet(c, rteam)
|
||||
if c.Err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
InviteMembers(rteam, ruser, teamSignup.Invites)
|
||||
|
||||
teamSignup.Team = *rteam
|
||||
teamSignup.User = *ruser
|
||||
|
||||
w.Write([]byte(teamSignup.ToJson()))
|
||||
}
|
||||
}
|
||||
|
||||
func createTeam(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
team := model.TeamFromJson(r.Body)
|
||||
|
||||
if team == nil {
|
||||
c.SetInvalidParam("createTeam", "team")
|
||||
return
|
||||
}
|
||||
|
||||
if utils.Cfg.ServiceSettings.Mode != utils.MODE_DEV {
|
||||
c.Err = model.NewAppError("createTeam", "The mode does not allow network creation without a valid invite", "")
|
||||
return
|
||||
}
|
||||
|
||||
if result := <-Srv.Store.Team().Save(team); result.Err != nil {
|
||||
c.Err = result.Err
|
||||
return
|
||||
} else {
|
||||
rteam := result.Data.(*model.Team)
|
||||
|
||||
channel := &model.Channel{DisplayName: "Town Square", Name: "town-square", Type: model.CHANNEL_OPEN, TeamId: rteam.Id}
|
||||
|
||||
if _, err := CreateChannel(c, channel, r.URL.Path, false); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
w.Write([]byte(rteam.ToJson()))
|
||||
}
|
||||
}
|
||||
|
||||
func doesSubDomainExist(subDomain string) bool {
|
||||
|
||||
// if it's configured for testing then skip this step
|
||||
if utils.Cfg.AWSSettings.Route53AccessKeyId == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
creds := aws.Creds(utils.Cfg.AWSSettings.Route53AccessKeyId, utils.Cfg.AWSSettings.Route53SecretAccessKey, "")
|
||||
r53 := route53.New(aws.DefaultConfig.Merge(&aws.Config{Credentials: creds, Region: utils.Cfg.AWSSettings.Route53Region}))
|
||||
|
||||
r53req := &route53.ListResourceRecordSetsInput{
|
||||
HostedZoneID: aws.String(utils.Cfg.AWSSettings.Route53ZoneId),
|
||||
MaxItems: aws.String("1"),
|
||||
StartRecordName: aws.String(fmt.Sprintf("%v.%v.", subDomain, utils.Cfg.ServiceSettings.Domain)),
|
||||
}
|
||||
|
||||
if result, err := r53.ListResourceRecordSets(r53req); err != nil {
|
||||
l4g.Error("error in doesSubDomainExist domain=%v err=%v", subDomain, err)
|
||||
return true
|
||||
} else {
|
||||
|
||||
for _, v := range result.ResourceRecordSets {
|
||||
if v.Name != nil && *v.Name == fmt.Sprintf("%v.%v.", subDomain, utils.Cfg.ServiceSettings.Domain) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func createSubDomain(subDomain string, target string) {
|
||||
|
||||
if utils.Cfg.AWSSettings.Route53AccessKeyId == "" {
|
||||
return
|
||||
}
|
||||
|
||||
creds := aws.Creds(utils.Cfg.AWSSettings.Route53AccessKeyId, utils.Cfg.AWSSettings.Route53SecretAccessKey, "")
|
||||
r53 := route53.New(aws.DefaultConfig.Merge(&aws.Config{Credentials: creds, Region: utils.Cfg.AWSSettings.Route53Region}))
|
||||
|
||||
rr := route53.ResourceRecord{
|
||||
Value: aws.String(target),
|
||||
}
|
||||
|
||||
rrs := make([]*route53.ResourceRecord, 1)
|
||||
rrs[0] = &rr
|
||||
|
||||
change := route53.Change{
|
||||
Action: aws.String("CREATE"),
|
||||
ResourceRecordSet: &route53.ResourceRecordSet{
|
||||
Name: aws.String(fmt.Sprintf("%v.%v", subDomain, utils.Cfg.ServiceSettings.Domain)),
|
||||
TTL: aws.Long(300),
|
||||
Type: aws.String("CNAME"),
|
||||
ResourceRecords: rrs,
|
||||
},
|
||||
}
|
||||
|
||||
changes := make([]*route53.Change, 1)
|
||||
changes[0] = &change
|
||||
|
||||
r53req := &route53.ChangeResourceRecordSetsInput{
|
||||
HostedZoneID: aws.String(utils.Cfg.AWSSettings.Route53ZoneId),
|
||||
ChangeBatch: &route53.ChangeBatch{
|
||||
Changes: changes,
|
||||
},
|
||||
}
|
||||
|
||||
if _, err := r53.ChangeResourceRecordSets(r53req); err != nil {
|
||||
l4g.Error("erro in createSubDomain domain=%v err=%v", subDomain, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func findTeamByDomain(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
m := model.MapFromJson(r.Body)
|
||||
|
||||
domain := strings.ToLower(strings.TrimSpace(m["domain"]))
|
||||
all := strings.ToLower(strings.TrimSpace(m["all"]))
|
||||
|
||||
found := FindTeamByDomain(c, domain, all)
|
||||
|
||||
if c.Err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if found {
|
||||
w.Write([]byte("true"))
|
||||
} else {
|
||||
w.Write([]byte("false"))
|
||||
}
|
||||
}
|
||||
|
||||
func FindTeamByDomain(c *Context, domain string, all string) bool {
|
||||
|
||||
if domain == "" || len(domain) > 64 {
|
||||
c.SetInvalidParam("findTeamByDomain", "domain")
|
||||
return false
|
||||
}
|
||||
|
||||
if model.IsReservedDomain(domain) {
|
||||
c.Err = model.NewAppError("findTeamByDomain", "This URL is unavailable. Please try another.", "d="+domain)
|
||||
return false
|
||||
}
|
||||
|
||||
if all == "false" {
|
||||
if result := <-Srv.Store.Team().GetByDomain(domain); result.Err != nil {
|
||||
return false
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
if doesSubDomainExist(domain) {
|
||||
return true
|
||||
}
|
||||
|
||||
protocol := "http"
|
||||
|
||||
if utils.Cfg.ServiceSettings.UseSSL {
|
||||
protocol = "https"
|
||||
}
|
||||
|
||||
for key, _ := range utils.Cfg.ServiceSettings.Shards {
|
||||
url := fmt.Sprintf("%v://%v.%v/api/v1", protocol, key, utils.Cfg.ServiceSettings.Domain)
|
||||
|
||||
if strings.Index(utils.Cfg.ServiceSettings.Domain, "localhost") == 0 {
|
||||
url = fmt.Sprintf("%v://%v/api/v1", protocol, utils.Cfg.ServiceSettings.Domain)
|
||||
}
|
||||
|
||||
client := model.NewClient(url)
|
||||
|
||||
if result, err := client.FindTeamByDomain(domain, false); err != nil {
|
||||
c.Err = err
|
||||
return false
|
||||
} else {
|
||||
if result.Data.(bool) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func findTeams(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
m := model.MapFromJson(r.Body)
|
||||
|
||||
email := strings.ToLower(strings.TrimSpace(m["email"]))
|
||||
|
||||
if email == "" {
|
||||
c.SetInvalidParam("findTeam", "email")
|
||||
return
|
||||
}
|
||||
|
||||
if result := <-Srv.Store.Team().GetTeamsForEmail(email); result.Err != nil {
|
||||
c.Err = result.Err
|
||||
return
|
||||
} else {
|
||||
teams := result.Data.([]*model.Team)
|
||||
|
||||
s := make([]string, 0, len(teams))
|
||||
|
||||
for _, v := range teams {
|
||||
s = append(s, v.Domain)
|
||||
}
|
||||
|
||||
w.Write([]byte(model.ArrayToJson(s)))
|
||||
}
|
||||
}
|
||||
|
||||
func emailTeams(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
m := model.MapFromJson(r.Body)
|
||||
|
||||
email := strings.ToLower(strings.TrimSpace(m["email"]))
|
||||
|
||||
if email == "" {
|
||||
c.SetInvalidParam("findTeam", "email")
|
||||
return
|
||||
}
|
||||
|
||||
protocol := "http"
|
||||
|
||||
if utils.Cfg.ServiceSettings.UseSSL {
|
||||
protocol = "https"
|
||||
}
|
||||
|
||||
subjectPage := NewServerTemplatePage("find_teams_subject", c.TeamUrl)
|
||||
bodyPage := NewServerTemplatePage("find_teams_body", c.TeamUrl)
|
||||
|
||||
for key, _ := range utils.Cfg.ServiceSettings.Shards {
|
||||
url := fmt.Sprintf("%v://%v.%v/api/v1", protocol, key, utils.Cfg.ServiceSettings.Domain)
|
||||
|
||||
if strings.Index(utils.Cfg.ServiceSettings.Domain, "localhost") == 0 {
|
||||
url = fmt.Sprintf("%v://%v/api/v1", protocol, utils.Cfg.ServiceSettings.Domain)
|
||||
}
|
||||
|
||||
client := model.NewClient(url)
|
||||
|
||||
if result, err := client.FindTeams(email); err != nil {
|
||||
l4g.Error("An error occured while finding teams at %v err=%v", key, err)
|
||||
} else {
|
||||
data := result.Data.([]string)
|
||||
for _, domain := range data {
|
||||
bodyPage.Props[fmt.Sprintf("%v://%v.%v", protocol, domain, utils.Cfg.ServiceSettings.Domain)] = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := utils.SendMail(email, subjectPage.Render(), bodyPage.Render()); err != nil {
|
||||
l4g.Error("An error occured while sending an email in emailTeams err=%v", err)
|
||||
}
|
||||
|
||||
w.Write([]byte(model.MapToJson(m)))
|
||||
}
|
||||
|
||||
func inviteMembers(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
invites := model.InvitesFromJson(r.Body)
|
||||
if len(invites.Invites) == 0 {
|
||||
c.Err = model.NewAppError("Team.InviteMembers", "No one to invite.", "")
|
||||
c.Err.StatusCode = http.StatusBadRequest
|
||||
return
|
||||
}
|
||||
|
||||
tchan := Srv.Store.Team().Get(c.Session.TeamId)
|
||||
uchan := Srv.Store.User().Get(c.Session.UserId)
|
||||
|
||||
var team *model.Team
|
||||
if result := <-tchan; result.Err != nil {
|
||||
c.Err = result.Err
|
||||
return
|
||||
} else {
|
||||
team = result.Data.(*model.Team)
|
||||
}
|
||||
|
||||
var user *model.User
|
||||
if result := <-uchan; result.Err != nil {
|
||||
c.Err = result.Err
|
||||
return
|
||||
} else {
|
||||
user = result.Data.(*model.User)
|
||||
}
|
||||
|
||||
ia := make([]string, len(invites.Invites))
|
||||
for _, invite := range invites.Invites {
|
||||
ia = append(ia, invite["email"])
|
||||
}
|
||||
|
||||
InviteMembers(team, user, ia)
|
||||
|
||||
w.Write([]byte(invites.ToJson()))
|
||||
}
|
||||
|
||||
func InviteMembers(team *model.Team, user *model.User, invites []string) {
|
||||
for _, invite := range invites {
|
||||
if len(invite) > 0 {
|
||||
teamUrl := ""
|
||||
if utils.Cfg.ServiceSettings.Mode == utils.MODE_DEV {
|
||||
teamUrl = "http://localhost:8065"
|
||||
} else if utils.Cfg.ServiceSettings.UseSSL {
|
||||
teamUrl = fmt.Sprintf("https://%v.%v", team.Domain, utils.Cfg.ServiceSettings.Domain)
|
||||
} else {
|
||||
teamUrl = fmt.Sprintf("http://%v.%v", team.Domain, utils.Cfg.ServiceSettings.Domain)
|
||||
}
|
||||
|
||||
sender := ""
|
||||
if len(strings.TrimSpace(user.FullName)) == 0 {
|
||||
sender = user.Username
|
||||
} else {
|
||||
sender = user.FullName
|
||||
}
|
||||
subjectPage := NewServerTemplatePage("invite_subject", teamUrl)
|
||||
subjectPage.Props["SenderName"] = sender
|
||||
subjectPage.Props["TeamName"] = team.Name
|
||||
bodyPage := NewServerTemplatePage("invite_body", teamUrl)
|
||||
bodyPage.Props["TeamName"] = team.Name
|
||||
bodyPage.Props["SenderName"] = sender
|
||||
|
||||
bodyPage.Props["Email"] = invite
|
||||
|
||||
props := make(map[string]string)
|
||||
props["email"] = invite
|
||||
props["id"] = team.Id
|
||||
props["name"] = team.Name
|
||||
props["domain"] = team.Domain
|
||||
props["time"] = fmt.Sprintf("%v", model.GetMillis())
|
||||
data := model.MapToJson(props)
|
||||
hash := model.HashPassword(fmt.Sprintf("%v:%v", data, utils.Cfg.ServiceSettings.InviteSalt))
|
||||
bodyPage.Props["Link"] = fmt.Sprintf("%s/signup_user_complete/?d=%s&h=%s", teamUrl, url.QueryEscape(data), url.QueryEscape(hash))
|
||||
|
||||
if utils.Cfg.ServiceSettings.Mode == utils.MODE_DEV {
|
||||
l4g.Info("sending invitation to %v %v", invite, bodyPage.Props["Link"])
|
||||
}
|
||||
|
||||
if err := utils.SendMail(invite, subjectPage.Render(), bodyPage.Render()); err != nil {
|
||||
l4g.Error("Failed to send invite email successfully err=%v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func updateTeamName(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
props := model.MapFromJson(r.Body)
|
||||
|
||||
new_name := props["new_name"]
|
||||
if len(new_name) == 0 {
|
||||
c.SetInvalidParam("updateTeamName", "new_name")
|
||||
return
|
||||
}
|
||||
|
||||
teamId := props["team_id"]
|
||||
if len(teamId) > 0 && len(teamId) != 26 {
|
||||
c.SetInvalidParam("updateTeamName", "team_id")
|
||||
return
|
||||
} else if len(teamId) == 0 {
|
||||
teamId = c.Session.TeamId
|
||||
}
|
||||
|
||||
if !c.HasPermissionsToTeam(teamId, "updateTeamName") {
|
||||
return
|
||||
}
|
||||
|
||||
if !strings.Contains(c.Session.Roles, model.ROLE_ADMIN) {
|
||||
c.Err = model.NewAppError("updateTeamName", "You do not have the appropriate permissions", "userId="+c.Session.UserId)
|
||||
c.Err.StatusCode = http.StatusForbidden
|
||||
return
|
||||
}
|
||||
|
||||
if result := <-Srv.Store.Team().UpdateName(new_name, c.Session.TeamId); result.Err != nil {
|
||||
c.Err = result.Err
|
||||
return
|
||||
}
|
||||
|
||||
w.Write([]byte(model.MapToJson(props)))
|
||||
}
|
||||
288
api/team_test.go
Обычный файл
288
api/team_test.go
Обычный файл
@@ -0,0 +1,288 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/mattermost/platform/model"
|
||||
"github.com/mattermost/platform/utils"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSignupTeam(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
_, err := Client.SignupTeam("test@nowhere.com", "name")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateFromSignupTeam(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
props := make(map[string]string)
|
||||
props["email"] = strings.ToLower(model.NewId()) + "corey@test.com"
|
||||
props["name"] = "Test Company name"
|
||||
props["time"] = fmt.Sprintf("%v", model.GetMillis())
|
||||
|
||||
data := model.MapToJson(props)
|
||||
hash := model.HashPassword(fmt.Sprintf("%v:%v", data, utils.Cfg.ServiceSettings.InviteSalt))
|
||||
|
||||
team := model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
user := model.User{Email: props["email"], FullName: "Corey Hulen", Password: "hello"}
|
||||
|
||||
ts := model.TeamSignup{Team: team, User: user, Invites: []string{"corey@test.com"}, Data: data, Hash: hash}
|
||||
|
||||
rts, err := Client.CreateTeamFromSignup(&ts)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if rts.Data.(*model.TeamSignup).Team.Name != team.Name {
|
||||
t.Fatal("full name didn't match")
|
||||
}
|
||||
|
||||
ruser := rts.Data.(*model.TeamSignup).User
|
||||
|
||||
if result, err := Client.LoginById(ruser.Id, user.Password); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
if result.Data.(*model.User).Email != user.Email {
|
||||
t.Fatal("email's didn't match")
|
||||
}
|
||||
}
|
||||
|
||||
ts.Data = "garbage"
|
||||
_, err = Client.CreateTeamFromSignup(&ts)
|
||||
if err == nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateTeam(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
team := model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
rteam, err := Client.CreateTeam(&team)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if rteam.Data.(*model.Team).Name != team.Name {
|
||||
t.Fatal("full name didn't match")
|
||||
}
|
||||
|
||||
if _, err := Client.CreateTeam(rteam.Data.(*model.Team)); err == nil {
|
||||
t.Fatal("Cannot create an existing")
|
||||
}
|
||||
|
||||
rteam.Data.(*model.Team).Id = ""
|
||||
if _, err := Client.CreateTeam(rteam.Data.(*model.Team)); err != nil {
|
||||
if err.Message != "A team with that domain already exists" {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := Client.DoPost("/teams/create", "garbage"); err == nil {
|
||||
t.Fatal("should have been an error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestFindTeamByEmail(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
|
||||
user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(user.Id)
|
||||
|
||||
if r1, err := Client.FindTeams(user.Email); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
domains := r1.Data.([]string)
|
||||
if domains[0] != team.Domain {
|
||||
t.Fatal(domains)
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := Client.FindTeams("missing"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
XXXXXX investigate and fix failing test
|
||||
|
||||
func TestFindTeamByDomain(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
|
||||
user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(user.Id)
|
||||
|
||||
if r1, err := Client.FindTeamByDomain(team.Domain, false); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
val := r1.Data.(bool)
|
||||
if !val {
|
||||
t.Fatal("should be a valid domain")
|
||||
}
|
||||
}
|
||||
|
||||
if r1, err := Client.FindTeamByDomain(team.Domain, true); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
val := r1.Data.(bool)
|
||||
if !val {
|
||||
t.Fatal("should be a valid domain")
|
||||
}
|
||||
}
|
||||
|
||||
if r1, err := Client.FindTeamByDomain("a"+model.NewId()+"a", false); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
val := r1.Data.(bool)
|
||||
if val {
|
||||
t.Fatal("shouldn't be a valid domain")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
func TestFindTeamByEmailSend(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
|
||||
user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(user.Id)
|
||||
|
||||
if _, err := Client.FindTeamsSendEmail(user.Email); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
}
|
||||
|
||||
if _, err := Client.FindTeamsSendEmail("missing"); err != nil {
|
||||
|
||||
// It should actually succeed at sending the email since it doesn't exist
|
||||
if !strings.Contains(err.DetailedError, "Failed to add to email address") {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestInviteMembers(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
|
||||
user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(user.Id)
|
||||
|
||||
Client.LoginByEmail(team.Domain, user.Email, "pwd")
|
||||
|
||||
invite := make(map[string]string)
|
||||
invite["email"] = model.NewId() + "corey@test.com"
|
||||
invite["first_name"] = "Test"
|
||||
invite["last_name"] = "Guy"
|
||||
invites := &model.Invites{Invites: []map[string]string{invite}}
|
||||
invites.Invites = append(invites.Invites, invite)
|
||||
|
||||
if _, err := Client.InviteMembers(invites); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
invites = &model.Invites{Invites: []map[string]string{}}
|
||||
if _, err := Client.InviteMembers(invites); err == nil {
|
||||
t.Fatal("Should have errored out on no invites to send")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateTeamName(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
|
||||
user := &model.User{TeamId: team.Id, Email: "test@nowhere.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(user.Id)
|
||||
|
||||
user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(user2.Id)
|
||||
|
||||
Client.LoginByEmail(team.Domain, user2.Email, "pwd")
|
||||
|
||||
data := make(map[string]string)
|
||||
data["new_name"] = "NewName"
|
||||
if _, err := Client.UpdateTeamName(data); err == nil {
|
||||
t.Fatal("Should have errored, not admin")
|
||||
}
|
||||
|
||||
Client.LoginByEmail(team.Domain, user.Email, "pwd")
|
||||
|
||||
data["new_name"] = ""
|
||||
if _, err := Client.UpdateTeamName(data); err == nil {
|
||||
t.Fatal("Should have errored, empty name")
|
||||
}
|
||||
|
||||
data["new_name"] = "NewName"
|
||||
if _, err := Client.UpdateTeamName(data); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// No GET team web service, so hard to confirm here that team name updated
|
||||
|
||||
data["team_id"] = "junk"
|
||||
if _, err := Client.UpdateTeamName(data); err == nil {
|
||||
t.Fatal("Should have errored, junk team id")
|
||||
}
|
||||
|
||||
data["team_id"] = "12345678901234567890123456"
|
||||
if _, err := Client.UpdateTeamName(data); err == nil {
|
||||
t.Fatal("Should have errored, bad team id")
|
||||
}
|
||||
|
||||
data["team_id"] = team.Id
|
||||
data["new_name"] = "NewNameAgain"
|
||||
if _, err := Client.UpdateTeamName(data); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// No GET team web service, so hard to confirm here that team name updated
|
||||
}
|
||||
|
||||
func TestFuzzyTeamCreate(t *testing.T) {
|
||||
|
||||
for i := 0; i < len(utils.FUZZY_STRINGS_NAMES) || i < len(utils.FUZZY_STRINGS_EMAILS); i++ {
|
||||
testName := "Name"
|
||||
testEmail := "test@nowhere.com"
|
||||
|
||||
if i < len(utils.FUZZY_STRINGS_NAMES) {
|
||||
testName = utils.FUZZY_STRINGS_NAMES[i]
|
||||
}
|
||||
if i < len(utils.FUZZY_STRINGS_EMAILS) {
|
||||
testEmail = utils.FUZZY_STRINGS_EMAILS[i]
|
||||
}
|
||||
|
||||
team := model.Team{Name: testName, Domain: "z-z-" + model.NewId() + "a", Email: testEmail, Type: model.TEAM_OPEN}
|
||||
|
||||
_, err := Client.CreateTeam(&team)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
54
api/templates/email_change_body.html
Обычный файл
54
api/templates/email_change_body.html
Обычный файл
@@ -0,0 +1,54 @@
|
||||
{{define "email_change_body"}}
|
||||
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="margin-top: 20px; line-height: 1.7; color: #555;">
|
||||
<tr>
|
||||
<td>
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 660px; font-family: Helvetica, Arial, sans-serif; font-size: 14px; background: #FFF;">
|
||||
<tr>
|
||||
<td style="border: 1px solid #ddd;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;">
|
||||
<tr>
|
||||
<td style="padding: 20px 30px 10px; text-align:left;">
|
||||
<img src="{{.TeamUrl}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="padding: 20px 50px 0; text-align: center; margin: 0 auto">
|
||||
<tr>
|
||||
<td style="border-bottom: 1px solid #ddd; padding: 0 0 20px;">
|
||||
<h2 style="font-weight: normal; margin-top: 10px;">You updated your email</h2>
|
||||
<p>You updated your email for {{.Props.TeamName}} on {{ .TeamUrl }}<br> If this change wasn't initiated by you, please reply to this email and let us know.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="color: #999; padding-top: 20px; line-height: 25px; font-size: 13px;">
|
||||
Any questions at all, mail us any time: <a href="mailto:{{.FeedbackEmail}}" style="text-decoration: none; color:#2389D7;">{{.FeedbackEmail}}</a>.<br>
|
||||
Best wishes,<br>
|
||||
The {{.SiteName}} Team<br>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="text-align: center;color: #AAA; font-size: 13px; padding-bottom: 10px;">
|
||||
<p style="margin: 25px 0;">
|
||||
<img width="65" src="{{.TeamUrl}}/static/images/circles.png" alt="">
|
||||
</p>
|
||||
<p style="padding: 0 50px;">
|
||||
(c) 2015 SpinPunch, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br>
|
||||
If you no longer wish to receive these emails, click on the following link: <a href="mailto:{{.FeedbackEmail}}?subject=Unsubscribe&body=Unsubscribe" style="text-decoration: none; color:#2389D7;">Unsubscribe</a>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
{{end}}
|
||||
|
||||
1
api/templates/email_change_subject.html
Обычный файл
1
api/templates/email_change_subject.html
Обычный файл
@@ -0,0 +1 @@
|
||||
{{define "email_change_subject"}}You updated your email for {{.Props.TeamName}} on {{ .Props.Domain }}{{end}}
|
||||
26
api/templates/error.html
Обычный файл
26
api/templates/error.html
Обычный файл
@@ -0,0 +1,26 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
||||
<title>{{ .SiteName }} - Error</title>
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css">
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/js/bootstrap.min.js"></script>
|
||||
<link href='https://fonts.googleapis.com/css?family=Lato:400,700,900' rel='stylesheet' type='text/css'>
|
||||
<link rel="stylesheet" href="/static/css/styles.css">
|
||||
</head>
|
||||
<body class="white error">
|
||||
<div class="container-fluid">
|
||||
<div class="error__container">
|
||||
<div class="error__icon"><i class="fa fa-exclamation-triangle"></i></div>
|
||||
<h2>{{ .SiteName }} needs your help:</h2>
|
||||
<p>{{.Message}}</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
<script>
|
||||
var details = "{{ .Details }}";
|
||||
if (details.length > 0) {
|
||||
console.log("error details: " + details);
|
||||
}
|
||||
</script>
|
||||
</html>
|
||||
64
api/templates/find_teams_body.html
Обычный файл
64
api/templates/find_teams_body.html
Обычный файл
@@ -0,0 +1,64 @@
|
||||
{{define "find_teams_body"}}
|
||||
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="margin-top: 20px; line-height: 1.7; color: #555;">
|
||||
<tr>
|
||||
<td>
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 660px; font-family: Helvetica, Arial, sans-serif; font-size: 14px; background: #FFF;">
|
||||
<tr>
|
||||
<td style="border: 1px solid #ddd;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;">
|
||||
<tr>
|
||||
<td style="padding: 20px 30px 10px; text-align:left;">
|
||||
<img src="{{.TeamUrl}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="padding: 20px 50px 0; text-align: center; margin: 0 auto">
|
||||
<tr>
|
||||
<td style="border-bottom: 1px solid #ddd; padding: 0 0 20px;">
|
||||
<h2 style="font-weight: normal; margin-top: 10px;">Finding teams</h2>
|
||||
<p>{{ if .Props }}
|
||||
The following teams were found:<br>
|
||||
{{range $index, $element := .Props}}
|
||||
{{ $index }}<br>
|
||||
{{ end }}
|
||||
{{ else }}
|
||||
We could not find any teams for the given email.
|
||||
{{ end }}
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="color: #999; padding-top: 20px; line-height: 25px; font-size: 13px;">
|
||||
Any questions at all, mail us any time: <a href="mailto:{{.FeedbackEmail}}" style="text-decoration: none; color:#2389D7;">{{.FeedbackEmail}}</a>.<br>
|
||||
Best wishes,<br>
|
||||
The {{.SiteName}} Team<br>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="text-align: center;color: #AAA; font-size: 13px; padding-bottom: 10px;">
|
||||
<p style="margin: 25px 0;">
|
||||
<img width="65" src="{{.TeamUrl}}/static/images/circles.png" alt="">
|
||||
</p>
|
||||
<p style="padding: 0 50px;">
|
||||
(c) 2015 SpinPunch, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br>
|
||||
If you no longer wish to receive these emails, click on the following link: <a href="mailto:{{.FeedbackEmail}}?subject=Unsubscribe&body=Unsubscribe" style="text-decoration: none; color:#2389D7;">Unsubscribe</a>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
{{end}}
|
||||
|
||||
|
||||
|
||||
1
api/templates/find_teams_subject.html
Обычный файл
1
api/templates/find_teams_subject.html
Обычный файл
@@ -0,0 +1 @@
|
||||
{{define "find_teams_subject"}}Your {{ .SiteName }} Teams{{end}}
|
||||
56
api/templates/invite_body.html
Обычный файл
56
api/templates/invite_body.html
Обычный файл
@@ -0,0 +1,56 @@
|
||||
{{define "invite_body"}}
|
||||
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="margin-top: 20px; line-height: 1.7; color: #555;">
|
||||
<tr>
|
||||
<td>
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 660px; font-family: Helvetica, Arial, sans-serif; font-size: 14px; background: #FFF;">
|
||||
<tr>
|
||||
<td style="border: 1px solid #ddd;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;">
|
||||
<tr>
|
||||
<td style="padding: 20px 30px 10px; text-align:left;">
|
||||
<img src="{{.TeamUrl}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="padding: 20px 50px 0; text-align: center; margin: 0 auto">
|
||||
<tr>
|
||||
<td style="border-bottom: 1px solid #ddd; padding: 0 0 20px;">
|
||||
<h2 style="font-weight: normal; margin-top: 10px;">You've been invited</h2>
|
||||
<p>{{.Props.TeamName}} started using {{.SiteName}}.<br> The team administrator <strong>{{.Props.SenderName}}</strong>, has invited you to join <strong>{{.Props.TeamName}}</strong>.</p>
|
||||
<p style="margin: 20px 0 15px">
|
||||
<a href="{{.Props.Link}}" style="background: #2389D7; border-radius: 3px; color: #fff; border: none; outline: none; min-width: 200px; padding: 15px 25px; font-size: 14px; font-family: inherit; cursor: pointer; -webkit-appearance: none;text-decoration: none;">Join Team</a>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="color: #999; padding-top: 20px; line-height: 25px; font-size: 13px;">
|
||||
Any questions at all, mail us any time: <a href="mailto:{{.FeedbackEmail}}" style="text-decoration: none; color:#2389D7;">{{.FeedbackEmail}}</a>.<br>
|
||||
Best wishes,<br>
|
||||
The {{.SiteName}} Team<br>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="text-align: center;color: #AAA; font-size: 13px; padding-bottom: 10px;">
|
||||
<p style="margin: 25px 0;">
|
||||
<img width="65" src="{{.TeamUrl}}/static/images/circles.png" alt="">
|
||||
</p>
|
||||
<p style="padding: 0 50px;">
|
||||
(c) 2015 SpinPunch, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br>
|
||||
If you no longer wish to receive these emails, click on the following link: <a href="mailto:{{.FeedbackEmail}}?subject=Unsubscribe&body=Unsubscribe" style="text-decoration: none; color:#2389D7;">Unsubscribe</a>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
{{end}}
|
||||
1
api/templates/invite_subject.html
Обычный файл
1
api/templates/invite_subject.html
Обычный файл
@@ -0,0 +1 @@
|
||||
{{define "invite_subject"}}{{ .Props.SenderName }} invited you to join {{ .Props.TeamName }} Team on {{.SiteName}}{{end}}
|
||||
55
api/templates/password_change_body.html
Обычный файл
55
api/templates/password_change_body.html
Обычный файл
@@ -0,0 +1,55 @@
|
||||
{{define "password_change_body"}}
|
||||
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="margin-top: 20px; line-height: 1.7; color: #555;">
|
||||
<tr>
|
||||
<td>
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 660px; font-family: Helvetica, Arial, sans-serif; font-size: 14px; background: #FFF;">
|
||||
<tr>
|
||||
<td style="border: 1px solid #ddd;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;">
|
||||
<tr>
|
||||
<td style="padding: 20px 30px 10px; text-align:left;">
|
||||
<img src="{{.TeamUrl}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="padding: 20px 50px 0; text-align: center; margin: 0 auto">
|
||||
<tr>
|
||||
<td style="border-bottom: 1px solid #ddd; padding: 0 0 20px;">
|
||||
<h2 style="font-weight: normal; margin-top: 10px;">You updated your password</h2>
|
||||
<p>You updated your password for {{.Props.TeamName}} on {{ .TeamUrl }} by {{.Props.Method}}.<br> If this change wasn't initiated by you, please reply to this email and let us know.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="color: #999; padding-top: 20px; line-height: 25px; font-size: 13px;">
|
||||
Any questions at all, mail us any time: <a href="mailto:{{.FeedbackEmail}}" style="text-decoration: none; color:#2389D7;">{{.FeedbackEmail}}</a>.<br>
|
||||
Best wishes,<br>
|
||||
The {{.SiteName}} Team<br>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="text-align: center;color: #AAA; font-size: 13px; padding-bottom: 10px;">
|
||||
<p style="margin: 25px 0;">
|
||||
<img width="65" src="{{.TeamUrl}}/static/images/circles.png" alt="">
|
||||
</p>
|
||||
<p style="padding: 0 50px;">
|
||||
(c) 2015 SpinPunch, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br>
|
||||
If you no longer wish to receive these emails, click on the following link: <a href="mailto:{{.FeedbackEmail}}?subject=Unsubscribe&body=Unsubscribe" style="text-decoration: none; color:#2389D7;">Unsubscribe</a>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
{{end}}
|
||||
|
||||
|
||||
1
api/templates/password_change_subject.html
Обычный файл
1
api/templates/password_change_subject.html
Обычный файл
@@ -0,0 +1 @@
|
||||
{{define "password_change_subject"}}You updated your password for {{.Props.TeamName}} on {{ .SiteName }}{{end}}
|
||||
57
api/templates/post_body.html
Обычный файл
57
api/templates/post_body.html
Обычный файл
@@ -0,0 +1,57 @@
|
||||
{{define "post_body"}}
|
||||
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="margin-top: 20px; line-height: 1.7; color: #555;">
|
||||
<tr>
|
||||
<td>
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 660px; font-family: Helvetica, Arial, sans-serif; font-size: 14px; background: #FFF;">
|
||||
<tr>
|
||||
<td style="border: 1px solid #ddd;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;">
|
||||
<tr>
|
||||
<td style="padding: 20px 30px 10px; text-align:left;">
|
||||
<img src="{{.TeamUrl}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="padding: 20px 50px 0; text-align: center; margin: 0 auto">
|
||||
<tr>
|
||||
<td style="border-bottom: 1px solid #ddd; padding: 0 0 20px;">
|
||||
<h2 style="font-weight: normal; margin-top: 10px;">You were mentioned</h2>
|
||||
<p>CHANNEL: {{.Props.ChannelName}}<br>{{.Props.SenderName}} - {{.Props.Hour}}:{{.Props.Minute}} GMT, {{.Props.Month}} {{.Props.Day}}<br><pre style="text-align:left;font-family: 'Lato', sans-serif;">{{.Props.PostMessage}}</pre></p>
|
||||
<p style="margin: 20px 0 15px">
|
||||
<a href="{{.Props.TeamLink}}" style="background: #2389D7; border-radius: 3px; color: #fff; border: none; outline: none; min-width: 200px; padding: 15px 25px; font-size: 14px; font-family: inherit; cursor: pointer; -webkit-appearance: none;text-decoration: none;">Go To Channel</a>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="color: #999; padding-top: 20px; line-height: 25px; font-size: 13px;">
|
||||
Any questions at all, mail us any time: <a href="mailto:{{.FeedbackEmail}}" style="text-decoration: none; color:#2389D7;">{{.FeedbackEmail}}</a>.<br>
|
||||
Best wishes,<br>
|
||||
The {{.SiteName}} Team<br>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="text-align: center;color: #AAA; font-size: 13px; padding-bottom: 10px;">
|
||||
<p style="margin: 25px 0;">
|
||||
<img width="65" src="{{.TeamUrl}}/static/images/circles.png" alt="">
|
||||
</p>
|
||||
<p style="padding: 0 50px;">
|
||||
(c) 2015 SpinPunch, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br>
|
||||
If you no longer wish to receive these emails, click on the following link: <a href="mailto:{{.FeedbackEmail}}?subject=Unsubscribe&body=Unsubscribe" style="text-decoration: none; color:#2389D7;">Unsubscribe</a>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
{{end}}
|
||||
|
||||
1
api/templates/post_subject.html
Обычный файл
1
api/templates/post_subject.html
Обычный файл
@@ -0,0 +1 @@
|
||||
{{define "post_subject"}}[{{.Props.TeamName}} {{.SiteName}}] {{.Props.SubjectText}} for {{.Props.Month}} {{.Props.Day}}, {{.Props.Year}}{{end}}
|
||||
58
api/templates/reset_body.html
Обычный файл
58
api/templates/reset_body.html
Обычный файл
@@ -0,0 +1,58 @@
|
||||
{{define "reset_body"}}
|
||||
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="margin-top: 20px; line-height: 1.7; color: #555;">
|
||||
<tr>
|
||||
<td>
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 660px; font-family: Helvetica, Arial, sans-serif; font-size: 14px; background: #FFF;">
|
||||
<tr>
|
||||
<td style="border: 1px solid #ddd;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;">
|
||||
<tr>
|
||||
<td style="padding: 20px 30px 10px; text-align:left;">
|
||||
<img src="{{.TeamUrl}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="padding: 20px 50px 0; text-align: center; margin: 0 auto">
|
||||
<tr>
|
||||
<td style="border-bottom: 1px solid #ddd; padding: 0 0 20px;">
|
||||
<h2 style="font-weight: normal; margin-top: 10px;">You requested a password reset</h2>
|
||||
<p>To change your password, click below.<br>If you did not mean to reset your password, please ignore this email and your password will remain the same.</p>
|
||||
<p style="margin: 20px 0 15px">
|
||||
<a href="{{.Props.ResetUrl}}" style="background: #2389D7; border-radius: 3px; color: #fff; border: none; outline: none; min-width: 200px; padding: 15px 25px; font-size: 14px; font-family: inherit; cursor: pointer; -webkit-appearance: none;text-decoration: none;">Reset Password</a>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="color: #999; padding-top: 20px; line-height: 25px; font-size: 13px;">
|
||||
Any questions at all, mail us any time: <a href="mailto:{{.FeedbackEmail}}" style="text-decoration: none; color:#2389D7;">{{.FeedbackEmail}}</a>.<br>
|
||||
Best wishes,<br>
|
||||
The {{.SiteName}} Team<br>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="text-align: center;color: #AAA; font-size: 13px; padding-bottom: 10px;">
|
||||
<p style="margin: 25px 0;">
|
||||
<img width="65" src="{{.TeamUrl}}/static/images/circles.png" alt="">
|
||||
</p>
|
||||
<p style="padding: 0 50px;">
|
||||
(c) 2015 SpinPunch, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br>
|
||||
If you no longer wish to receive these emails, click on the following link: <a href="mailto:{{.FeedbackEmail}}?subject=Unsubscribe&body=Unsubscribe" style="text-decoration: none; color:#2389D7;">Unsubscribe</a>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
{{end}}
|
||||
|
||||
|
||||
1
api/templates/reset_subject.html
Обычный файл
1
api/templates/reset_subject.html
Обычный файл
@@ -0,0 +1 @@
|
||||
{{define "reset_subject"}}Reset your password{{end}}
|
||||
59
api/templates/signup_team_body.html
Обычный файл
59
api/templates/signup_team_body.html
Обычный файл
@@ -0,0 +1,59 @@
|
||||
{{define "signup_team_body"}}
|
||||
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="margin-top: 20px; line-height: 1.7; color: #555;">
|
||||
<tr>
|
||||
<td>
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 660px; font-family: Helvetica, Arial, sans-serif; font-size: 14px; background: #FFF;">
|
||||
<tr>
|
||||
<td style="border: 1px solid #ddd;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;">
|
||||
<tr>
|
||||
<td style="padding: 20px 30px 10px; text-align:left;">
|
||||
<img src="{{.TeamUrl}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="padding: 20px 50px 0; text-align: center; margin: 0 auto">
|
||||
<tr>
|
||||
<td style="border-bottom: 1px solid #ddd; padding: 0 0 20px;">
|
||||
<h2 style="font-weight: normal; margin-top: 10px;">Thanks for creating a team!</h2>
|
||||
<p style="margin: 20px 0 25px">
|
||||
<a href="{{.Props.Link}}" style="background: #2389D7; border-radius: 3px; color: #fff; border: none; outline: none; min-width: 200px; padding: 15px 25px; font-size: 14px; font-family: inherit; cursor: pointer; -webkit-appearance: none;text-decoration: none;">Set up your team</a>
|
||||
</p>
|
||||
{{ .SiteName }} is free for an unlimited time, for unlimited users.<br>You'll get more out of {{ .SiteName }} when your team is in constant communication--let's get them on board.<br></p>
|
||||
<p>
|
||||
Learn more by <a href="{{.Props.TourUrl}}" style="text-decoration: none; color:#2389D7;">taking a tour</a>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="color: #999; padding-top: 20px; line-height: 25px; font-size: 13px;">
|
||||
Any questions at all, mail us any time: <a href="mailto:{{.FeedbackEmail}}" style="text-decoration: none; color:#2389D7;">{{.FeedbackEmail}}</a>.<br>
|
||||
Best wishes,<br>
|
||||
The {{.SiteName}} Team<br>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="text-align: center;color: #AAA; font-size: 13px; padding-bottom: 10px;">
|
||||
<p style="margin: 25px 0;">
|
||||
<img width="65" src="{{.TeamUrl}}/static/images/circles.png" alt="">
|
||||
</p>
|
||||
<p style="padding: 0 50px;">
|
||||
(c) 2015 SpinPunch, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br>
|
||||
If you no longer wish to receive these emails, click on the following link: <a href="mailto:{{.FeedbackEmail}}?subject=Unsubscribe&body=Unsubscribe" style="text-decoration: none; color:#2389D7;">Unsubscribe</a>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
{{end}}
|
||||
1
api/templates/signup_team_subject.html
Обычный файл
1
api/templates/signup_team_subject.html
Обычный файл
@@ -0,0 +1 @@
|
||||
{{define "signup_team_subject"}}Invitation to {{ .SiteName }}{{end}}
|
||||
56
api/templates/verify_body.html
Обычный файл
56
api/templates/verify_body.html
Обычный файл
@@ -0,0 +1,56 @@
|
||||
{{define "verify_body"}}
|
||||
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="margin-top: 20px; line-height: 1.7; color: #555;">
|
||||
<tr>
|
||||
<td>
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 660px; font-family: Helvetica, Arial, sans-serif; font-size: 14px; background: #FFF;">
|
||||
<tr>
|
||||
<td style="border: 1px solid #ddd;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;">
|
||||
<tr>
|
||||
<td style="padding: 20px 30px 10px; text-align:left;">
|
||||
<img src="{{.TeamUrl}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="padding: 20px 50px 0; text-align: center; margin: 0 auto">
|
||||
<tr>
|
||||
<td style="border-bottom: 1px solid #ddd; padding: 0 0 20px;">
|
||||
<h2 style="font-weight: normal; margin-top: 10px;">You've been invited</h2>
|
||||
<p>Please verify your email address by clicking below.</p>
|
||||
<p style="margin: 20px 0 15px">
|
||||
<a href="{{.Props.VerifyUrl}}" style="background: #2389D7; border-radius: 3px; color: #fff; border: none; outline: none; min-width: 200px; padding: 15px 25px; font-size: 14px; font-family: inherit; cursor: pointer; -webkit-appearance: none;text-decoration: none;">Verify Email</a>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="color: #999; padding-top: 20px; line-height: 25px; font-size: 13px;">
|
||||
Any questions at all, mail us any time: <a href="mailto:{{.FeedbackEmail}}" style="text-decoration: none; color:#2389D7;">{{.FeedbackEmail}}</a>.<br>
|
||||
Best wishes,<br>
|
||||
The {{.SiteName}} Team<br>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="text-align: center;color: #AAA; font-size: 13px; padding-bottom: 10px;">
|
||||
<p style="margin: 25px 0;">
|
||||
<img width="65" src="{{.TeamUrl}}/static/images/circles.png" alt="">
|
||||
</p>
|
||||
<p style="padding: 0 50px;">
|
||||
(c) 2015 SpinPunch, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br>
|
||||
If you no longer wish to receive these emails, click on the following link: <a href="mailto:{{.FeedbackEmail}}?subject=Unsubscribe&body=Unsubscribe" style="text-decoration: none; color:#2389D7;">Unsubscribe</a>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
{{end}}
|
||||
1
api/templates/verify_subject.html
Обычный файл
1
api/templates/verify_subject.html
Обычный файл
@@ -0,0 +1 @@
|
||||
{{define "verify_subject"}}[{{ .Props.TeamName }} {{ .SiteName }}] Email Verification{{end}}
|
||||
54
api/templates/welcome_body.html
Обычный файл
54
api/templates/welcome_body.html
Обычный файл
@@ -0,0 +1,54 @@
|
||||
{{define "welcome_body"}}
|
||||
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="margin-top: 20px; line-height: 1.7; color: #555;">
|
||||
<tr>
|
||||
<td>
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 660px; font-family: Helvetica, Arial, sans-serif; font-size: 14px; background: #FFF;">
|
||||
<tr>
|
||||
<td style="border: 1px solid #ddd;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;">
|
||||
<tr>
|
||||
<td style="padding: 20px 30px 10px; text-align:left;">
|
||||
<img src="{{.TeamUrl}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="padding: 20px 50px 0; text-align: center; margin: 0 auto">
|
||||
<tr>
|
||||
<td style="border-bottom: 1px solid #ddd; padding: 0 0 20px;">
|
||||
<h2 style="font-weight: normal; margin-top: 10px;">You joined the {{.Props.TeamName}} team at {{.SiteName}}!</h2>
|
||||
<p>Please let me know if you have any questions.<br>Enjoy your stay at <a href="{{.TeamUrl}}">{{.SiteName}}</a>.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="color: #999; padding-top: 20px; line-height: 25px; font-size: 13px;">
|
||||
Any questions at all, mail us any time: <a href="mailto:{{.FeedbackEmail}}" style="text-decoration: none; color:#2389D7;">{{.FeedbackEmail}}</a>.<br>
|
||||
Best wishes,<br>
|
||||
The {{.SiteName}} Team<br>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="text-align: center;color: #AAA; font-size: 13px; padding-bottom: 10px;">
|
||||
<p style="margin: 25px 0;">
|
||||
<img width="65" src="{{.TeamUrl}}/static/images/circles.png" alt="">
|
||||
</p>
|
||||
<p style="padding: 0 50px;">
|
||||
(c) 2015 SpinPunch, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br>
|
||||
If you no longer wish to receive these emails, click on the following link: <a href="mailto:{{.FeedbackEmail}}?subject=Unsubscribe&body=Unsubscribe" style="text-decoration: none; color:#2389D7;">Unsubscribe</a>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
{{end}}
|
||||
|
||||
1
api/templates/welcome_subject.html
Обычный файл
1
api/templates/welcome_subject.html
Обычный файл
@@ -0,0 +1 @@
|
||||
{{define "welcome_subject"}}Welcome to {{ .SiteName }}{{end}}
|
||||
1258
api/user.go
Обычный файл
1258
api/user.go
Обычный файл
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
960
api/user_test.go
Обычный файл
960
api/user_test.go
Обычный файл
@@ -0,0 +1,960 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/goamz/goamz/aws"
|
||||
"github.com/goamz/goamz/s3"
|
||||
"github.com/mattermost/platform/model"
|
||||
"github.com/mattermost/platform/utils"
|
||||
"image/color"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestCreateUser(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
team := model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
rteam, _ := Client.CreateTeam(&team)
|
||||
|
||||
user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", FullName: "Corey Hulen", Password: "hello"}
|
||||
|
||||
ruser, err := Client.CreateUser(&user, "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if ruser.Data.(*model.User).FullName != user.FullName {
|
||||
t.Fatal("full name didn't match")
|
||||
}
|
||||
|
||||
if ruser.Data.(*model.User).Password != "" {
|
||||
t.Fatal("password wasn't blank")
|
||||
}
|
||||
|
||||
if _, err := Client.CreateUser(ruser.Data.(*model.User), ""); err == nil {
|
||||
t.Fatal("Cannot create an existing")
|
||||
}
|
||||
|
||||
ruser.Data.(*model.User).Id = ""
|
||||
if _, err := Client.CreateUser(ruser.Data.(*model.User), ""); err != nil {
|
||||
if err.Message != "An account with that email already exists." {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
ruser.Data.(*model.User).Email = ""
|
||||
if _, err := Client.CreateUser(ruser.Data.(*model.User), ""); err != nil {
|
||||
if err.Message != "Invalid email" {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
user2 := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", FullName: "Corey Hulen", Password: "hello", Username: model.BOT_USERNAME}
|
||||
|
||||
if _, err := Client.CreateUser(&user2, ""); err == nil {
|
||||
t.Fatal("Should have failed using reserved bot name")
|
||||
}
|
||||
|
||||
if _, err := Client.DoPost("/users/create", "garbage"); err == nil {
|
||||
t.Fatal("should have been an error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateUserAllowedDomains(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
team := model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_INVITE, AllowedDomains: "spinpunch.com, @nowh.com,@hello.com"}
|
||||
rteam, _ := Client.CreateTeam(&team)
|
||||
|
||||
user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", FullName: "Corey Hulen", Password: "hello"}
|
||||
|
||||
_, err := Client.CreateUser(&user, "")
|
||||
if err == nil {
|
||||
t.Fatal("should have failed")
|
||||
}
|
||||
|
||||
user.Email = "test@nowh.com"
|
||||
_, err = Client.CreateUser(&user, "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLogin(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
team := model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
rteam, _ := Client.CreateTeam(&team)
|
||||
|
||||
user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
ruser, _ := Client.CreateUser(&user, "")
|
||||
Srv.Store.User().VerifyEmail(ruser.Data.(*model.User).Id)
|
||||
|
||||
if result, err := Client.LoginById(ruser.Data.(*model.User).Id, user.Password); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
if result.Data.(*model.User).Email != user.Email {
|
||||
t.Fatal("email's didn't match")
|
||||
}
|
||||
}
|
||||
|
||||
if result, err := Client.LoginByEmail(team.Domain, user.Email, user.Password); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
if result.Data.(*model.User).Email != user.Email {
|
||||
t.Fatal("emails didn't match")
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := Client.LoginByEmail(team.Domain, user.Email, user.Password+"invalid"); err == nil {
|
||||
t.Fatal("Invalid Password")
|
||||
}
|
||||
|
||||
if _, err := Client.LoginByEmail(team.Domain, "", user.Password); err == nil {
|
||||
t.Fatal("should have failed")
|
||||
}
|
||||
|
||||
authToken := Client.AuthToken
|
||||
Client.AuthToken = "invalid"
|
||||
|
||||
if _, err := Client.GetUser(ruser.Data.(*model.User).Id, ""); err == nil {
|
||||
t.Fatal("should have failed")
|
||||
}
|
||||
|
||||
Client.AuthToken = ""
|
||||
|
||||
team2 := model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_INVITE}
|
||||
rteam2 := Client.Must(Client.CreateTeam(&team2))
|
||||
|
||||
user2 := model.User{TeamId: rteam2.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
|
||||
if _, err := Client.CreateUserFromSignup(&user2, "junk", "1231312"); err == nil {
|
||||
t.Fatal("Should have errored, signed up without hashed email")
|
||||
}
|
||||
|
||||
props := make(map[string]string)
|
||||
props["email"] = user2.Email
|
||||
props["id"] = rteam2.Data.(*model.Team).Id
|
||||
props["name"] = rteam2.Data.(*model.Team).Name
|
||||
props["time"] = fmt.Sprintf("%v", model.GetMillis())
|
||||
data := model.MapToJson(props)
|
||||
hash := model.HashPassword(fmt.Sprintf("%v:%v", data, utils.Cfg.ServiceSettings.InviteSalt))
|
||||
|
||||
ruser2, _ := Client.CreateUserFromSignup(&user2, data, hash)
|
||||
|
||||
if _, err := Client.LoginByEmail(team2.Domain, ruser2.Data.(*model.User).Email, user2.Password); err != nil {
|
||||
t.Fatal("From verfied hash")
|
||||
}
|
||||
|
||||
Client.AuthToken = authToken
|
||||
}
|
||||
|
||||
func TestSessions(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
team := model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
rteam, _ := Client.CreateTeam(&team)
|
||||
|
||||
user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
ruser := Client.Must(Client.CreateUser(&user, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(ruser.Id)
|
||||
|
||||
deviceId := model.NewId()
|
||||
Client.LoginByEmailWithDevice(team.Domain, user.Email, user.Password, deviceId)
|
||||
Client.LoginByEmail(team.Domain, user.Email, user.Password)
|
||||
|
||||
r1, err := Client.GetSessions(ruser.Id)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
sessions := r1.Data.([]*model.Session)
|
||||
otherSession := ""
|
||||
|
||||
if len(sessions) != 2 {
|
||||
t.Fatal("invalid number of sessions")
|
||||
}
|
||||
|
||||
for _, session := range sessions {
|
||||
if session.DeviceId == deviceId {
|
||||
otherSession = session.AltId
|
||||
}
|
||||
|
||||
if len(session.Id) != 0 {
|
||||
t.Fatal("shouldn't return sessions")
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := Client.RevokeSession(otherSession); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
r2, err := Client.GetSessions(ruser.Id)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
sessions2 := r2.Data.([]*model.Session)
|
||||
|
||||
if len(sessions2) != 1 {
|
||||
t.Fatal("invalid number of sessions")
|
||||
}
|
||||
|
||||
if _, err := Client.RevokeSession(otherSession); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestGetUser(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
team := model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
rteam, _ := Client.CreateTeam(&team)
|
||||
|
||||
user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
ruser, _ := Client.CreateUser(&user, "")
|
||||
Srv.Store.User().VerifyEmail(ruser.Data.(*model.User).Id)
|
||||
|
||||
user2 := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
ruser2, _ := Client.CreateUser(&user2, "")
|
||||
Srv.Store.User().VerifyEmail(ruser2.Data.(*model.User).Id)
|
||||
|
||||
Client.LoginByEmail(team.Domain, user.Email, user.Password)
|
||||
|
||||
rId := ruser.Data.(*model.User).Id
|
||||
if result, err := Client.GetUser(rId, ""); err != nil {
|
||||
t.Fatal("Failed to get user")
|
||||
} else {
|
||||
if result.Data.(*model.User).Password != "" {
|
||||
t.Fatal("User shouldn't have any password data once set")
|
||||
}
|
||||
|
||||
if cache_result, err := Client.GetUser(rId, result.Etag); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if cache_result.Data.(*model.User) != nil {
|
||||
t.Fatal("cache should be empty")
|
||||
}
|
||||
}
|
||||
|
||||
if result, err := Client.GetMe(""); err != nil {
|
||||
t.Fatal("Failed to get user")
|
||||
} else {
|
||||
if result.Data.(*model.User).Password != "" {
|
||||
t.Fatal("User shouldn't have any password data once set")
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := Client.GetUser("FORBIDDENERROR", ""); err == nil {
|
||||
t.Fatal("shouldn't exist")
|
||||
}
|
||||
|
||||
if _, err := Client.GetUser(ruser2.Data.(*model.User).Id, ""); err == nil {
|
||||
t.Fatal("shouldn't have accss")
|
||||
}
|
||||
|
||||
if userMap, err := Client.GetProfiles(rteam.Data.(*model.Team).Id, ""); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if len(userMap.Data.(map[string]*model.User)) != 2 {
|
||||
t.Fatal("should have been 2")
|
||||
} else if userMap.Data.(map[string]*model.User)[rId].Id != rId {
|
||||
t.Fatal("should have been valid")
|
||||
} else {
|
||||
|
||||
// test etag caching
|
||||
if cache_result, err := Client.GetProfiles(rteam.Data.(*model.Team).Id, userMap.Etag); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if cache_result.Data.(map[string]*model.User) != nil {
|
||||
t.Log(cache_result.Data)
|
||||
t.Fatal("cache should be empty")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Client.AuthToken = ""
|
||||
if _, err := Client.GetUser(ruser2.Data.(*model.User).Id, ""); err == nil {
|
||||
t.Fatal("shouldn't have accss")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAudits(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
team := model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
rteam, _ := Client.CreateTeam(&team)
|
||||
|
||||
user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
ruser, _ := Client.CreateUser(&user, "")
|
||||
Srv.Store.User().VerifyEmail(ruser.Data.(*model.User).Id)
|
||||
|
||||
Client.LoginByEmail(team.Domain, user.Email, user.Password)
|
||||
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
|
||||
if result, err := Client.GetAudits(ruser.Data.(*model.User).Id, ""); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
|
||||
if len(result.Data.(model.Audits)) != 2 {
|
||||
t.Fatal(result.Data.(model.Audits))
|
||||
}
|
||||
|
||||
if cache_result, err := Client.GetAudits(ruser.Data.(*model.User).Id, result.Etag); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if cache_result.Data.(model.Audits) != nil {
|
||||
t.Fatal("cache should be empty")
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := Client.GetAudits("FORBIDDENERROR", ""); err == nil {
|
||||
t.Fatal("audit log shouldn't exist")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUserCreateImage(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
i := createProfileImage("Corey Hulen", "eo1zkdr96pdj98pjmq8zy35wba")
|
||||
if i == nil {
|
||||
t.Fatal("Failed to gen image")
|
||||
}
|
||||
|
||||
colorful := color.RGBA{116, 49, 196, 255}
|
||||
|
||||
if i.RGBAAt(1, 1) != colorful {
|
||||
t.Fatal("Failed to create correct color")
|
||||
}
|
||||
|
||||
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
|
||||
user := &model.User{TeamId: team.Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(user.Id)
|
||||
|
||||
Client.LoginByEmail(team.Domain, user.Email, "pwd")
|
||||
|
||||
Client.DoGet("/users/"+user.Id+"/image", "", "")
|
||||
|
||||
}
|
||||
|
||||
func TestUserUploadProfileImage(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
|
||||
user := &model.User{TeamId: team.Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(user.Id)
|
||||
|
||||
if utils.IsS3Configured() {
|
||||
|
||||
body := &bytes.Buffer{}
|
||||
writer := multipart.NewWriter(body)
|
||||
|
||||
if _, upErr := Client.UploadFile("/users/newimage", body.Bytes(), writer.FormDataContentType()); upErr == nil {
|
||||
t.Fatal("Should have errored")
|
||||
}
|
||||
|
||||
Client.LoginByEmail(team.Domain, user.Email, "pwd")
|
||||
|
||||
if _, upErr := Client.UploadFile("/users/newimage", body.Bytes(), writer.FormDataContentType()); upErr == nil {
|
||||
t.Fatal("Should have errored")
|
||||
}
|
||||
|
||||
part, err := writer.CreateFormFile("blargh", "test.png")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
path := utils.FindDir("web/static/images")
|
||||
file, err := os.Open(path + "/test.png")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
_, err = io.Copy(part, file)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := writer.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if _, upErr := Client.UploadFile("/users/newimage", body.Bytes(), writer.FormDataContentType()); upErr == nil {
|
||||
t.Fatal("Should have errored")
|
||||
}
|
||||
|
||||
file2, err := os.Open(path + "/test.png")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer file2.Close()
|
||||
|
||||
body = &bytes.Buffer{}
|
||||
writer = multipart.NewWriter(body)
|
||||
|
||||
part, err = writer.CreateFormFile("image", "test.png")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if _, err := io.Copy(part, file2); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := writer.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if _, upErr := Client.UploadFile("/users/newimage", body.Bytes(), writer.FormDataContentType()); upErr != nil {
|
||||
t.Fatal(upErr)
|
||||
}
|
||||
|
||||
Client.DoGet("/users/"+user.Id+"/image", "", "")
|
||||
|
||||
var auth aws.Auth
|
||||
auth.AccessKey = utils.Cfg.AWSSettings.S3AccessKeyId
|
||||
auth.SecretKey = utils.Cfg.AWSSettings.S3SecretAccessKey
|
||||
|
||||
s := s3.New(auth, aws.Regions[utils.Cfg.AWSSettings.S3Region])
|
||||
bucket := s.Bucket(utils.Cfg.AWSSettings.S3Bucket)
|
||||
|
||||
if err := bucket.Del("teams/" + user.TeamId + "/users/" + user.Id + "/profile.png"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
} else {
|
||||
body := &bytes.Buffer{}
|
||||
writer := multipart.NewWriter(body)
|
||||
if _, upErr := Client.UploadFile("/users/newimage", body.Bytes(), writer.FormDataContentType()); upErr.StatusCode != http.StatusNotImplemented {
|
||||
t.Fatal("Should have failed with 501 - Not Implemented")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestUserUpdate(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
|
||||
time1 := model.GetMillis()
|
||||
|
||||
user := &model.User{TeamId: team.Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", FullName: "Corey Hulen", Password: "pwd", LastActivityAt: time1, LastPingAt: time1, Roles: ""}
|
||||
user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(user.Id)
|
||||
|
||||
if _, err := Client.UpdateUser(user); err == nil {
|
||||
t.Fatal("Should have errored")
|
||||
}
|
||||
|
||||
Client.LoginByEmail(team.Domain, user.Email, "pwd")
|
||||
|
||||
time2 := model.GetMillis()
|
||||
|
||||
user.FullName = "Jim Jimmy"
|
||||
user.TeamId = "12345678901234567890123456"
|
||||
user.LastActivityAt = time2
|
||||
user.LastPingAt = time2
|
||||
user.Roles = model.ROLE_ADMIN
|
||||
user.LastPasswordUpdate = 123
|
||||
|
||||
if result, err := Client.UpdateUser(user); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
if result.Data.(*model.User).FullName != "Jim Jimmy" {
|
||||
t.Fatal("FullName did not update properly")
|
||||
}
|
||||
if result.Data.(*model.User).TeamId != team.Id {
|
||||
t.Fatal("TeamId should not have updated")
|
||||
}
|
||||
if result.Data.(*model.User).LastActivityAt != time1 {
|
||||
t.Fatal("LastActivityAt should not have updated")
|
||||
}
|
||||
if result.Data.(*model.User).LastPingAt != time1 {
|
||||
t.Fatal("LastPingAt should not have updated")
|
||||
}
|
||||
if result.Data.(*model.User).Roles != "" {
|
||||
t.Fatal("Roles should not have updated")
|
||||
}
|
||||
if result.Data.(*model.User).LastPasswordUpdate == 123 {
|
||||
t.Fatal("LastPasswordUpdate should not have updated")
|
||||
}
|
||||
}
|
||||
|
||||
user.TeamId = "junk"
|
||||
if _, err := Client.UpdateUser(user); err == nil {
|
||||
t.Fatal("Should have errored - tried to change teamId to junk")
|
||||
}
|
||||
|
||||
user.TeamId = team.Id
|
||||
if _, err := Client.UpdateUser(nil); err == nil {
|
||||
t.Fatal("Should have errored")
|
||||
}
|
||||
|
||||
user2 := &model.User{TeamId: team.Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(user2.Id)
|
||||
|
||||
Client.LoginByEmail(team.Domain, user2.Email, "pwd")
|
||||
|
||||
user.FullName = "Tim Timmy"
|
||||
|
||||
if _, err := Client.UpdateUser(user); err == nil {
|
||||
t.Fatal("Should have errored")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUserUpdatePassword(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
|
||||
user := &model.User{TeamId: team.Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(user.Id)
|
||||
|
||||
if _, err := Client.UpdateUserPassword(user.Id, "pwd", "newpwd"); err == nil {
|
||||
t.Fatal("Should have errored")
|
||||
}
|
||||
|
||||
Client.LoginByEmail(team.Domain, user.Email, "pwd")
|
||||
|
||||
if _, err := Client.UpdateUserPassword("123", "pwd", "newpwd"); err == nil {
|
||||
t.Fatal("Should have errored")
|
||||
}
|
||||
|
||||
if _, err := Client.UpdateUserPassword(user.Id, "", "newpwd"); err == nil {
|
||||
t.Fatal("Should have errored")
|
||||
}
|
||||
|
||||
if _, err := Client.UpdateUserPassword(user.Id, "pwd", "npwd"); err == nil {
|
||||
t.Fatal("Should have errored")
|
||||
}
|
||||
|
||||
if _, err := Client.UpdateUserPassword("12345678901234567890123456", "pwd", "newpwd"); err == nil {
|
||||
t.Fatal("Should have errored")
|
||||
}
|
||||
|
||||
if _, err := Client.UpdateUserPassword(user.Id, "badpwd", "newpwd"); err == nil {
|
||||
t.Fatal("Should have errored")
|
||||
}
|
||||
|
||||
if _, err := Client.UpdateUserPassword(user.Id, "pwd", "newpwd"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
updatedUser := Client.Must(Client.GetUser(user.Id, "")).Data.(*model.User)
|
||||
if updatedUser.LastPasswordUpdate == user.LastPasswordUpdate {
|
||||
t.Fatal("LastPasswordUpdate should have changed")
|
||||
}
|
||||
|
||||
if _, err := Client.LoginByEmail(team.Domain, user.Email, "newpwd"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
user2 := &model.User{TeamId: team.Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User)
|
||||
|
||||
Client.LoginByEmail(team.Domain, user2.Email, "pwd")
|
||||
|
||||
if _, err := Client.UpdateUserPassword(user.Id, "pwd", "newpwd"); err == nil {
|
||||
t.Fatal("Should have errored")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUserUpdateRoles(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
|
||||
user := &model.User{TeamId: team.Id, Email: "test@nowhere.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(user.Id)
|
||||
|
||||
user2 := &model.User{TeamId: team.Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(user2.Id)
|
||||
|
||||
data := make(map[string]string)
|
||||
data["user_id"] = user.Id
|
||||
data["new_roles"] = ""
|
||||
|
||||
if _, err := Client.UpdateUserRoles(data); err == nil {
|
||||
t.Fatal("Should have errored, not logged in")
|
||||
}
|
||||
|
||||
Client.LoginByEmail(team.Domain, user2.Email, "pwd")
|
||||
|
||||
if _, err := Client.UpdateUserRoles(data); err == nil {
|
||||
t.Fatal("Should have errored, not admin")
|
||||
}
|
||||
|
||||
team2 := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team2 = Client.Must(Client.CreateTeam(team2)).Data.(*model.Team)
|
||||
|
||||
user3 := &model.User{TeamId: team2.Id, Email: "test@nowhere.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
user3 = Client.Must(Client.CreateUser(user3, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(user3.Id)
|
||||
|
||||
Client.LoginByEmail(team2.Domain, user3.Email, "pwd")
|
||||
|
||||
data["user_id"] = user2.Id
|
||||
|
||||
if _, err := Client.UpdateUserRoles(data); err == nil {
|
||||
t.Fatal("Should have errored, wrong team")
|
||||
}
|
||||
|
||||
Client.LoginByEmail(team.Domain, user.Email, "pwd")
|
||||
|
||||
data["user_id"] = "junk"
|
||||
data["new_roles"] = "admin"
|
||||
|
||||
if _, err := Client.UpdateUserRoles(data); err == nil {
|
||||
t.Fatal("Should have errored, bad id")
|
||||
}
|
||||
|
||||
data["user_id"] = "12345678901234567890123456"
|
||||
|
||||
if _, err := Client.UpdateUserRoles(data); err == nil {
|
||||
t.Fatal("Should have errored, bad id")
|
||||
}
|
||||
|
||||
data["user_id"] = user2.Id
|
||||
|
||||
if result, err := Client.UpdateUserRoles(data); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
if result.Data.(*model.User).Roles != "admin" {
|
||||
t.Fatal("Roles did not update properly")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestUserUpdateActive(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
|
||||
user := &model.User{TeamId: team.Id, Email: "test@nowhere.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(user.Id)
|
||||
|
||||
user2 := &model.User{TeamId: team.Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(user2.Id)
|
||||
|
||||
if _, err := Client.UpdateActive(user.Id, false); err == nil {
|
||||
t.Fatal("Should have errored, not logged in")
|
||||
}
|
||||
|
||||
Client.LoginByEmail(team.Domain, user2.Email, "pwd")
|
||||
|
||||
if _, err := Client.UpdateActive(user.Id, false); err == nil {
|
||||
t.Fatal("Should have errored, not admin")
|
||||
}
|
||||
|
||||
team2 := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team2 = Client.Must(Client.CreateTeam(team2)).Data.(*model.Team)
|
||||
|
||||
user3 := &model.User{TeamId: team2.Id, Email: "test@nowhere.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
user3 = Client.Must(Client.CreateUser(user3, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(user3.Id)
|
||||
|
||||
Client.LoginByEmail(team2.Domain, user3.Email, "pwd")
|
||||
|
||||
if _, err := Client.UpdateActive(user.Id, false); err == nil {
|
||||
t.Fatal("Should have errored, wrong team")
|
||||
}
|
||||
|
||||
Client.LoginByEmail(team.Domain, user.Email, "pwd")
|
||||
|
||||
if _, err := Client.UpdateActive("junk", false); err == nil {
|
||||
t.Fatal("Should have errored, bad id")
|
||||
}
|
||||
|
||||
if _, err := Client.UpdateActive("12345678901234567890123456", false); err == nil {
|
||||
t.Fatal("Should have errored, bad id")
|
||||
}
|
||||
|
||||
if result, err := Client.UpdateActive(user2.Id, false); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
if result.Data.(*model.User).DeleteAt == 0 {
|
||||
t.Fatal("active did not update properly")
|
||||
}
|
||||
}
|
||||
|
||||
if result, err := Client.UpdateActive(user2.Id, true); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
if result.Data.(*model.User).DeleteAt != 0 {
|
||||
t.Fatal("active did not update properly true")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSendPasswordReset(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
|
||||
user := &model.User{TeamId: team.Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(user.Id)
|
||||
|
||||
data := make(map[string]string)
|
||||
data["email"] = user.Email
|
||||
data["domain"] = team.Domain
|
||||
|
||||
if _, err := Client.SendPasswordReset(data); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
data["email"] = ""
|
||||
if _, err := Client.SendPasswordReset(data); err == nil {
|
||||
t.Fatal("Should have errored - no email")
|
||||
}
|
||||
|
||||
data["email"] = "junk@junk.com"
|
||||
if _, err := Client.SendPasswordReset(data); err == nil {
|
||||
t.Fatal("Should have errored - bad email")
|
||||
}
|
||||
|
||||
data["email"] = user.Email
|
||||
data["domain"] = ""
|
||||
if _, err := Client.SendPasswordReset(data); err == nil {
|
||||
t.Fatal("Should have errored - no domain")
|
||||
}
|
||||
|
||||
data["domain"] = "junk"
|
||||
if _, err := Client.SendPasswordReset(data); err == nil {
|
||||
t.Fatal("Should have errored - bad domain")
|
||||
}
|
||||
}
|
||||
|
||||
func TestResetPassword(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
|
||||
user := &model.User{TeamId: team.Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(user.Id)
|
||||
|
||||
data := make(map[string]string)
|
||||
data["new_password"] = "newpwd"
|
||||
props := make(map[string]string)
|
||||
props["user_id"] = user.Id
|
||||
props["time"] = fmt.Sprintf("%v", model.GetMillis())
|
||||
data["data"] = model.MapToJson(props)
|
||||
data["hash"] = model.HashPassword(fmt.Sprintf("%v:%v", data["data"], utils.Cfg.ServiceSettings.ResetSalt))
|
||||
data["domain"] = team.Domain
|
||||
|
||||
if _, err := Client.ResetPassword(data); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
data["new_password"] = ""
|
||||
if _, err := Client.ResetPassword(data); err == nil {
|
||||
t.Fatal("Should have errored - no password")
|
||||
}
|
||||
|
||||
data["new_password"] = "npwd"
|
||||
if _, err := Client.ResetPassword(data); err == nil {
|
||||
t.Fatal("Should have errored - password too short")
|
||||
}
|
||||
|
||||
data["new_password"] = "newpwd"
|
||||
data["hash"] = ""
|
||||
if _, err := Client.ResetPassword(data); err == nil {
|
||||
t.Fatal("Should have errored - no hash")
|
||||
}
|
||||
|
||||
data["hash"] = "junk"
|
||||
if _, err := Client.ResetPassword(data); err == nil {
|
||||
t.Fatal("Should have errored - bad hash")
|
||||
}
|
||||
|
||||
props["user_id"] = ""
|
||||
data["data"] = model.MapToJson(props)
|
||||
if _, err := Client.ResetPassword(data); err == nil {
|
||||
t.Fatal("Should have errored - no user id")
|
||||
}
|
||||
|
||||
data["user_id"] = "12345678901234567890123456"
|
||||
data["data"] = model.MapToJson(props)
|
||||
if _, err := Client.ResetPassword(data); err == nil {
|
||||
t.Fatal("Should have errored - bad user id")
|
||||
}
|
||||
|
||||
props["user_id"] = user.Id
|
||||
props["time"] = ""
|
||||
data["data"] = model.MapToJson(props)
|
||||
if _, err := Client.ResetPassword(data); err == nil {
|
||||
t.Fatal("Should have errored - no time")
|
||||
}
|
||||
|
||||
props["time"] = fmt.Sprintf("%v", model.GetMillis())
|
||||
data["data"] = model.MapToJson(props)
|
||||
data["domain"] = ""
|
||||
if _, err := Client.ResetPassword(data); err == nil {
|
||||
t.Fatal("Should have errored - no domain")
|
||||
}
|
||||
|
||||
data["domain"] = "junk"
|
||||
if _, err := Client.ResetPassword(data); err == nil {
|
||||
t.Fatal("Should have errored - bad domain")
|
||||
}
|
||||
|
||||
team2 := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test2@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team2 = Client.Must(Client.CreateTeam(team2)).Data.(*model.Team)
|
||||
|
||||
data["domain"] = team2.Domain
|
||||
if _, err := Client.ResetPassword(data); err == nil {
|
||||
t.Fatal("Should have errored - domain team doesn't match user team")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUserUpdateNotify(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
|
||||
user := &model.User{TeamId: team.Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", FullName: "Corey Hulen", Password: "pwd", Roles: ""}
|
||||
user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(user.Id)
|
||||
|
||||
data := make(map[string]string)
|
||||
data["user_id"] = user.Id
|
||||
data["email"] = "true"
|
||||
data["desktop"] = "all"
|
||||
data["desktop_sound"] = "false"
|
||||
|
||||
if _, err := Client.UpdateUserNotify(data); err == nil {
|
||||
t.Fatal("Should have errored - not logged in")
|
||||
}
|
||||
|
||||
Client.LoginByEmail(team.Domain, user.Email, "pwd")
|
||||
|
||||
if result, err := Client.UpdateUserNotify(data); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
if result.Data.(*model.User).NotifyProps["desktop"] != data["desktop"] {
|
||||
t.Fatal("NotifyProps did not update properly - desktop")
|
||||
}
|
||||
if result.Data.(*model.User).NotifyProps["desktop_sound"] != data["desktop_sound"] {
|
||||
t.Fatal("NotifyProps did not update properly - desktop_sound")
|
||||
}
|
||||
if result.Data.(*model.User).NotifyProps["email"] != data["email"] {
|
||||
t.Fatal("NotifyProps did not update properly - email")
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := Client.UpdateUserNotify(nil); err == nil {
|
||||
t.Fatal("Should have errored")
|
||||
}
|
||||
|
||||
data["user_id"] = "junk"
|
||||
if _, err := Client.UpdateUserNotify(data); err == nil {
|
||||
t.Fatal("Should have errored - junk user id")
|
||||
}
|
||||
|
||||
data["user_id"] = "12345678901234567890123456"
|
||||
if _, err := Client.UpdateUserNotify(data); err == nil {
|
||||
t.Fatal("Should have errored - bad user id")
|
||||
}
|
||||
|
||||
data["user_id"] = user.Id
|
||||
data["desktop"] = ""
|
||||
if _, err := Client.UpdateUserNotify(data); err == nil {
|
||||
t.Fatal("Should have errored - empty desktop notify")
|
||||
}
|
||||
|
||||
data["desktop"] = "all"
|
||||
data["desktop_sound"] = ""
|
||||
if _, err := Client.UpdateUserNotify(data); err == nil {
|
||||
t.Fatal("Should have errored - empty desktop sound")
|
||||
}
|
||||
|
||||
data["desktop_sound"] = "false"
|
||||
data["email"] = ""
|
||||
if _, err := Client.UpdateUserNotify(data); err == nil {
|
||||
t.Fatal("Should have errored - empty email")
|
||||
}
|
||||
}
|
||||
|
||||
func TestFuzzyUserCreate(t *testing.T) {
|
||||
|
||||
team := model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
rteam, _ := Client.CreateTeam(&team)
|
||||
|
||||
for i := 0; i < len(utils.FUZZY_STRINGS_NAMES) || i < len(utils.FUZZY_STRINGS_EMAILS); i++ {
|
||||
testName := "Name"
|
||||
testEmail := "test@nowhere.com"
|
||||
|
||||
if i < len(utils.FUZZY_STRINGS_NAMES) {
|
||||
testName = utils.FUZZY_STRINGS_NAMES[i]
|
||||
}
|
||||
if i < len(utils.FUZZY_STRINGS_EMAILS) {
|
||||
testEmail = utils.FUZZY_STRINGS_EMAILS[i]
|
||||
}
|
||||
|
||||
user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + testEmail, FullName: testName, Password: "hello"}
|
||||
|
||||
_, err := Client.CreateUser(&user, "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStatuses(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
team := model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
rteam, _ := Client.CreateTeam(&team)
|
||||
|
||||
user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
ruser := Client.Must(Client.CreateUser(&user, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(ruser.Id)
|
||||
|
||||
Client.LoginByEmail(team.Domain, user.Email, user.Password)
|
||||
|
||||
r1, err := Client.GetStatuses()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
statuses := r1.Data.(map[string]string)
|
||||
|
||||
if len(statuses) != 1 {
|
||||
t.Fatal("invalid number of statuses")
|
||||
}
|
||||
|
||||
for _, status := range statuses {
|
||||
if status != model.USER_OFFLINE && status != model.USER_AWAY && status != model.USER_ONLINE {
|
||||
t.Fatal("one of the statuses had an invalid value")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
132
api/web_conn.go
Обычный файл
132
api/web_conn.go
Обычный файл
@@ -0,0 +1,132 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
l4g "code.google.com/p/log4go"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/mattermost/platform/model"
|
||||
"github.com/mattermost/platform/store"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
WRITE_WAIT = 10 * time.Second
|
||||
PONG_WAIT = 60 * time.Second
|
||||
PING_PERIOD = (PONG_WAIT * 9) / 10
|
||||
MAX_SIZE = 512
|
||||
REDIS_WAIT = 60 * time.Second
|
||||
)
|
||||
|
||||
type WebConn struct {
|
||||
WebSocket *websocket.Conn
|
||||
Send chan *model.Message
|
||||
TeamId string
|
||||
UserId string
|
||||
ChannelAccessCache map[string]bool
|
||||
}
|
||||
|
||||
func NewWebConn(ws *websocket.Conn, teamId string, userId string, sessionId string) *WebConn {
|
||||
go func() {
|
||||
achan := Srv.Store.User().UpdateUserAndSessionActivity(userId, sessionId, model.GetMillis())
|
||||
pchan := Srv.Store.User().UpdateLastPingAt(userId, model.GetMillis())
|
||||
|
||||
if result := <-achan; result.Err != nil {
|
||||
l4g.Error("Failed to update LastActivityAt for user_id=%v and session_id=%v, err=%v", userId, sessionId, result.Err)
|
||||
}
|
||||
|
||||
if result := <-pchan; result.Err != nil {
|
||||
l4g.Error("Failed to updated LastPingAt for user_id=%v, err=%v", userId, result.Err)
|
||||
}
|
||||
}()
|
||||
|
||||
return &WebConn{Send: make(chan *model.Message, 64), WebSocket: ws, UserId: userId, TeamId: teamId, ChannelAccessCache: make(map[string]bool)}
|
||||
}
|
||||
|
||||
func (c *WebConn) readPump() {
|
||||
defer func() {
|
||||
hub.Unregister(c)
|
||||
c.WebSocket.Close()
|
||||
}()
|
||||
c.WebSocket.SetReadLimit(MAX_SIZE)
|
||||
c.WebSocket.SetReadDeadline(time.Now().Add(PONG_WAIT))
|
||||
c.WebSocket.SetPongHandler(func(string) error {
|
||||
c.WebSocket.SetReadDeadline(time.Now().Add(PONG_WAIT))
|
||||
|
||||
go func() {
|
||||
if result := <-Srv.Store.User().UpdateLastPingAt(c.UserId, model.GetMillis()); result.Err != nil {
|
||||
l4g.Error("Failed to updated LastPingAt for user_id=%v, err=%v", c.UserId, result.Err)
|
||||
}
|
||||
}()
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
for {
|
||||
var msg model.Message
|
||||
if err := c.WebSocket.ReadJSON(&msg); err != nil {
|
||||
return
|
||||
} else {
|
||||
msg.TeamId = c.TeamId
|
||||
msg.UserId = c.UserId
|
||||
store.PublishAndForget(&msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *WebConn) writePump() {
|
||||
ticker := time.NewTicker(PING_PERIOD)
|
||||
|
||||
defer func() {
|
||||
ticker.Stop()
|
||||
c.WebSocket.Close()
|
||||
}()
|
||||
|
||||
for {
|
||||
select {
|
||||
case msg, ok := <-c.Send:
|
||||
if !ok {
|
||||
c.WebSocket.SetWriteDeadline(time.Now().Add(WRITE_WAIT))
|
||||
c.WebSocket.WriteMessage(websocket.CloseMessage, []byte{})
|
||||
return
|
||||
}
|
||||
|
||||
if len(msg.ChannelId) > 0 {
|
||||
allowed, ok := c.ChannelAccessCache[msg.ChannelId]
|
||||
if !ok {
|
||||
allowed = hasPermissionsToChannel(Srv.Store.Channel().CheckPermissionsTo(c.TeamId, msg.ChannelId, c.UserId))
|
||||
c.ChannelAccessCache[msg.ChannelId] = allowed
|
||||
}
|
||||
|
||||
if allowed {
|
||||
c.WebSocket.SetWriteDeadline(time.Now().Add(WRITE_WAIT))
|
||||
if err := c.WebSocket.WriteJSON(msg); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
} else {
|
||||
c.WebSocket.SetWriteDeadline(time.Now().Add(WRITE_WAIT))
|
||||
if err := c.WebSocket.WriteJSON(msg); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
case <-ticker.C:
|
||||
c.WebSocket.SetWriteDeadline(time.Now().Add(WRITE_WAIT))
|
||||
if err := c.WebSocket.WriteMessage(websocket.PingMessage, []byte{}); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func hasPermissionsToChannel(sc store.StoreChannel) bool {
|
||||
if cresult := <-sc; cresult.Err != nil {
|
||||
return false
|
||||
} else if cresult.Data.(int64) != 1 {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
71
api/web_hub.go
Обычный файл
71
api/web_hub.go
Обычный файл
@@ -0,0 +1,71 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
l4g "code.google.com/p/log4go"
|
||||
)
|
||||
|
||||
type Hub struct {
|
||||
teamHubs map[string]*TeamHub
|
||||
register chan *WebConn
|
||||
unregister chan *WebConn
|
||||
stop chan string
|
||||
}
|
||||
|
||||
var hub = &Hub{
|
||||
register: make(chan *WebConn),
|
||||
unregister: make(chan *WebConn),
|
||||
teamHubs: make(map[string]*TeamHub),
|
||||
stop: make(chan string),
|
||||
}
|
||||
|
||||
func (h *Hub) Register(webConn *WebConn) {
|
||||
h.register <- webConn
|
||||
}
|
||||
|
||||
func (h *Hub) Unregister(webConn *WebConn) {
|
||||
h.unregister <- webConn
|
||||
}
|
||||
|
||||
func (h *Hub) Stop(teamId string) {
|
||||
h.stop <- teamId
|
||||
}
|
||||
|
||||
func (h *Hub) Start() {
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
|
||||
case c := <-h.register:
|
||||
nh := h.teamHubs[c.TeamId]
|
||||
|
||||
if nh == nil {
|
||||
nh = NewTeamHub(c.TeamId)
|
||||
h.teamHubs[c.TeamId] = nh
|
||||
nh.Start()
|
||||
}
|
||||
|
||||
nh.Register(c)
|
||||
|
||||
case c := <-h.unregister:
|
||||
if nh, ok := h.teamHubs[c.TeamId]; ok {
|
||||
nh.Unregister(c)
|
||||
}
|
||||
|
||||
case s := <-h.stop:
|
||||
if len(s) == 0 {
|
||||
l4g.Debug("stopping all connections")
|
||||
for _, v := range h.teamHubs {
|
||||
v.Stop()
|
||||
}
|
||||
return
|
||||
} else if nh, ok := h.teamHubs[s]; ok {
|
||||
delete(h.teamHubs, s)
|
||||
nh.Stop()
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
40
api/web_socket.go
Обычный файл
40
api/web_socket.go
Обычный файл
@@ -0,0 +1,40 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
l4g "code.google.com/p/log4go"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/mattermost/platform/model"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func InitWebSocket(r *mux.Router) {
|
||||
l4g.Debug("Initializing web socket api routes")
|
||||
r.Handle("/websocket", ApiUserRequired(connect)).Methods("GET")
|
||||
hub.Start()
|
||||
}
|
||||
|
||||
func connect(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
upgrader := websocket.Upgrader{
|
||||
ReadBufferSize: 1024,
|
||||
WriteBufferSize: 1024,
|
||||
CheckOrigin: func(r *http.Request) bool {
|
||||
return true
|
||||
},
|
||||
}
|
||||
|
||||
ws, err := upgrader.Upgrade(w, r, nil)
|
||||
if err != nil {
|
||||
l4g.Error("websocket connect err: %v", err)
|
||||
c.Err = model.NewAppError("connect", "Failed to upgrade websocket connection", "")
|
||||
return
|
||||
}
|
||||
|
||||
wc := NewWebConn(ws, c.Session.TeamId, c.Session.UserId, c.Session.Id)
|
||||
hub.Register(wc)
|
||||
go wc.writePump()
|
||||
wc.readPump()
|
||||
}
|
||||
129
api/web_socket_test.go
Обычный файл
129
api/web_socket_test.go
Обычный файл
@@ -0,0 +1,129 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/mattermost/platform/model"
|
||||
"github.com/mattermost/platform/utils"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestSocket(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
url := "ws://localhost:" + utils.Cfg.ServiceSettings.Port + "/api/v1/websocket"
|
||||
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
|
||||
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(user1.Id)
|
||||
Client.LoginByEmail(team.Domain, user1.Email, "pwd")
|
||||
|
||||
channel1 := &model.Channel{DisplayName: "Test Web Scoket 1", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
|
||||
|
||||
channel2 := &model.Channel{DisplayName: "Test Web Scoket 2", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channel2 = Client.Must(Client.CreateChannel(channel2)).Data.(*model.Channel)
|
||||
|
||||
header1 := http.Header{}
|
||||
header1.Set(model.HEADER_AUTH, "BEARER "+Client.AuthToken)
|
||||
|
||||
c1, _, err := websocket.DefaultDialer.Dial(url, header1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
|
||||
user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User)
|
||||
Srv.Store.User().VerifyEmail(user2.Id)
|
||||
Client.LoginByEmail(team.Domain, user2.Email, "pwd")
|
||||
|
||||
header2 := http.Header{}
|
||||
header2.Set(model.HEADER_AUTH, "BEARER "+Client.AuthToken)
|
||||
|
||||
c2, _, err := websocket.DefaultDialer.Dial(url, header2)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
Client.Must(Client.JoinChannel(channel1.Id))
|
||||
|
||||
// Read the join channel message that gets generated
|
||||
var rmsg model.Message
|
||||
if err := c2.ReadJSON(&rmsg); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Test sending message without a channelId
|
||||
m := model.NewMessage("", "", "", model.ACTION_TYPING)
|
||||
m.Add("RootId", model.NewId())
|
||||
m.Add("ParentId", model.NewId())
|
||||
|
||||
c1.WriteJSON(m)
|
||||
|
||||
if err := c2.ReadJSON(&rmsg); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if team.Id != rmsg.TeamId {
|
||||
t.Fatal("Ids do not match")
|
||||
}
|
||||
|
||||
if m.Props["RootId"] != rmsg.Props["RootId"] {
|
||||
t.Fatal("Ids do not match")
|
||||
}
|
||||
|
||||
// Test sending messsage to Channel you have access to
|
||||
m = model.NewMessage("", channel1.Id, "", model.ACTION_TYPING)
|
||||
m.Add("RootId", model.NewId())
|
||||
m.Add("ParentId", model.NewId())
|
||||
|
||||
c1.WriteJSON(m)
|
||||
|
||||
if err := c2.ReadJSON(&rmsg); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if team.Id != rmsg.TeamId {
|
||||
t.Fatal("Ids do not match")
|
||||
}
|
||||
|
||||
if m.Props["RootId"] != rmsg.Props["RootId"] {
|
||||
t.Fatal("Ids do not match")
|
||||
}
|
||||
|
||||
// Test sending message to Channel you *do not* have access too
|
||||
m = model.NewMessage("", channel2.Id, "", model.ACTION_TYPING)
|
||||
m.Add("RootId", model.NewId())
|
||||
m.Add("ParentId", model.NewId())
|
||||
|
||||
c1.WriteJSON(m)
|
||||
|
||||
go func() {
|
||||
if err := c2.ReadJSON(&rmsg); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Fatal(err)
|
||||
}()
|
||||
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
hub.Stop(team.Id)
|
||||
|
||||
}
|
||||
|
||||
func TestZZWebScoketTearDown(t *testing.T) {
|
||||
// *IMPORTANT* - Kind of hacky
|
||||
// This should be the last function in any test file
|
||||
// that calls Setup()
|
||||
// Should be in the last file too sorted by name
|
||||
time.Sleep(2 * time.Second)
|
||||
TearDown()
|
||||
}
|
||||
119
api/web_team_hub.go
Обычный файл
119
api/web_team_hub.go
Обычный файл
@@ -0,0 +1,119 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
l4g "code.google.com/p/log4go"
|
||||
"github.com/mattermost/platform/model"
|
||||
"github.com/mattermost/platform/store"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type TeamHub struct {
|
||||
connections map[*WebConn]bool
|
||||
broadcast chan *model.Message
|
||||
register chan *WebConn
|
||||
unregister chan *WebConn
|
||||
stop chan bool
|
||||
teamId string
|
||||
}
|
||||
|
||||
func NewTeamHub(teamId string) *TeamHub {
|
||||
return &TeamHub{
|
||||
broadcast: make(chan *model.Message),
|
||||
register: make(chan *WebConn),
|
||||
unregister: make(chan *WebConn),
|
||||
connections: make(map[*WebConn]bool),
|
||||
stop: make(chan bool),
|
||||
teamId: teamId,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *TeamHub) Register(webConn *WebConn) {
|
||||
h.register <- webConn
|
||||
}
|
||||
|
||||
func (h *TeamHub) Unregister(webConn *WebConn) {
|
||||
h.unregister <- webConn
|
||||
}
|
||||
|
||||
func (h *TeamHub) Stop() {
|
||||
h.stop <- true
|
||||
}
|
||||
|
||||
func (h *TeamHub) Start() {
|
||||
|
||||
pubsub := store.RedisClient().PubSub()
|
||||
|
||||
go func() {
|
||||
defer func() {
|
||||
l4g.Debug("redis reader finished for teamId=%v", h.teamId)
|
||||
hub.Stop(h.teamId)
|
||||
}()
|
||||
|
||||
l4g.Debug("redis reader starting for teamId=%v", h.teamId)
|
||||
|
||||
err := pubsub.Subscribe(h.teamId)
|
||||
if err != nil {
|
||||
l4g.Error("Error while subscribing to redis %v %v", h.teamId, err)
|
||||
return
|
||||
}
|
||||
|
||||
for {
|
||||
if payload, err := pubsub.ReceiveTimeout(REDIS_WAIT); err != nil {
|
||||
if strings.Contains(err.Error(), "i/o timeout") {
|
||||
if len(h.connections) == 0 {
|
||||
l4g.Debug("No active connections so sending stop %v", h.teamId)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
msg := store.GetMessageFromPayload(payload)
|
||||
if msg != nil {
|
||||
h.broadcast <- msg
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}()
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case webCon := <-h.register:
|
||||
h.connections[webCon] = true
|
||||
case webCon := <-h.unregister:
|
||||
if _, ok := h.connections[webCon]; ok {
|
||||
delete(h.connections, webCon)
|
||||
close(webCon.Send)
|
||||
}
|
||||
case msg := <-h.broadcast:
|
||||
for webCon := range h.connections {
|
||||
if !(webCon.UserId == msg.UserId && msg.Action == model.ACTION_TYPING) {
|
||||
select {
|
||||
case webCon.Send <- msg:
|
||||
default:
|
||||
close(webCon.Send)
|
||||
delete(h.connections, webCon)
|
||||
}
|
||||
}
|
||||
}
|
||||
case s := <-h.stop:
|
||||
if s {
|
||||
|
||||
l4g.Debug("team hub stopping for teamId=%v", h.teamId)
|
||||
|
||||
for webCon := range h.connections {
|
||||
webCon.WebSocket.Close()
|
||||
}
|
||||
|
||||
pubsub.Close()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
Ссылка в новой задаче
Block a user