first commit
Этот коммит содержится в:
39
model/audit.go
Обычный файл
39
model/audit.go
Обычный файл
@@ -0,0 +1,39 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
)
|
||||
|
||||
type Audit struct {
|
||||
Id string `json:"id"`
|
||||
CreateAt int64 `json:"create_at"`
|
||||
UserId string `json:"user_id"`
|
||||
Action string `json:"action"`
|
||||
ExtraInfo string `json:"extra_info"`
|
||||
IpAddress string `json:"ip_address"`
|
||||
SessionId string `json:"session_id"`
|
||||
}
|
||||
|
||||
func (o *Audit) ToJson() string {
|
||||
b, err := json.Marshal(o)
|
||||
if err != nil {
|
||||
return ""
|
||||
} else {
|
||||
return string(b)
|
||||
}
|
||||
}
|
||||
|
||||
func AuditFromJson(data io.Reader) *Audit {
|
||||
decoder := json.NewDecoder(data)
|
||||
var o Audit
|
||||
err := decoder.Decode(&o)
|
||||
if err == nil {
|
||||
return &o
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
19
model/audit_test.go
Обычный файл
19
model/audit_test.go
Обычный файл
@@ -0,0 +1,19 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAuditJson(t *testing.T) {
|
||||
audit := Audit{Id: NewId(), UserId: NewId(), CreateAt: GetMillis()}
|
||||
json := audit.ToJson()
|
||||
result := AuditFromJson(strings.NewReader(json))
|
||||
|
||||
if audit.Id != result.Id {
|
||||
t.Fatal("Ids do not match")
|
||||
}
|
||||
}
|
||||
39
model/audits.go
Обычный файл
39
model/audits.go
Обычный файл
@@ -0,0 +1,39 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
)
|
||||
|
||||
type Audits []Audit
|
||||
|
||||
func (o Audits) Etag() string {
|
||||
if len(o) > 0 {
|
||||
// the first in the list is always the most current
|
||||
return Etag(o[0].CreateAt)
|
||||
} else {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func (o Audits) ToJson() string {
|
||||
if b, err := json.Marshal(o); err != nil {
|
||||
return "[]"
|
||||
} else {
|
||||
return string(b)
|
||||
}
|
||||
}
|
||||
|
||||
func AuditsFromJson(data io.Reader) Audits {
|
||||
decoder := json.NewDecoder(data)
|
||||
var o Audits
|
||||
err := decoder.Decode(&o)
|
||||
if err == nil {
|
||||
return o
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
29
model/audits_test.go
Обычный файл
29
model/audits_test.go
Обычный файл
@@ -0,0 +1,29 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAuditsJson(t *testing.T) {
|
||||
audit := Audit{Id: NewId(), UserId: NewId(), CreateAt: GetMillis()}
|
||||
json := audit.ToJson()
|
||||
result := AuditFromJson(strings.NewReader(json))
|
||||
|
||||
if audit.Id != result.Id {
|
||||
t.Fatal("Ids do not match")
|
||||
}
|
||||
|
||||
var audits Audits = make([]Audit, 1)
|
||||
audits[0] = audit
|
||||
|
||||
ljson := audits.ToJson()
|
||||
results := AuditsFromJson(strings.NewReader(ljson))
|
||||
|
||||
if audits[0].Id != results[0].Id {
|
||||
t.Fatal("Ids do not match")
|
||||
}
|
||||
}
|
||||
104
model/channel.go
Обычный файл
104
model/channel.go
Обычный файл
@@ -0,0 +1,104 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
)
|
||||
|
||||
const (
|
||||
CHANNEL_OPEN = "O"
|
||||
CHANNEL_PRIVATE = "P"
|
||||
CHANNEL_DIRECT = "D"
|
||||
DEFAULT_CHANNEL = "town-square"
|
||||
)
|
||||
|
||||
type Channel struct {
|
||||
Id string `json:"id"`
|
||||
CreateAt int64 `json:"create_at"`
|
||||
UpdateAt int64 `json:"update_at"`
|
||||
DeleteAt int64 `json:"delete_at"`
|
||||
TeamId string `json:"team_id"`
|
||||
Type string `json:"type"`
|
||||
DisplayName string `json:"display_name"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
LastPostAt int64 `json:"last_post_at"`
|
||||
TotalMsgCount int64 `json:"total_msg_count"`
|
||||
}
|
||||
|
||||
func (o *Channel) ToJson() string {
|
||||
b, err := json.Marshal(o)
|
||||
if err != nil {
|
||||
return ""
|
||||
} else {
|
||||
return string(b)
|
||||
}
|
||||
}
|
||||
|
||||
func ChannelFromJson(data io.Reader) *Channel {
|
||||
decoder := json.NewDecoder(data)
|
||||
var o Channel
|
||||
err := decoder.Decode(&o)
|
||||
if err == nil {
|
||||
return &o
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (o *Channel) Etag() string {
|
||||
return Etag(o.Id, o.LastPostAt)
|
||||
}
|
||||
|
||||
func (o *Channel) IsValid() *AppError {
|
||||
|
||||
if len(o.Id) != 26 {
|
||||
return NewAppError("Channel.IsValid", "Invalid Id", "")
|
||||
}
|
||||
|
||||
if o.CreateAt == 0 {
|
||||
return NewAppError("Channel.IsValid", "Create at must be a valid time", "id="+o.Id)
|
||||
}
|
||||
|
||||
if o.UpdateAt == 0 {
|
||||
return NewAppError("Channel.IsValid", "Update at must be a valid time", "id="+o.Id)
|
||||
}
|
||||
|
||||
if len(o.DisplayName) > 64 {
|
||||
return NewAppError("Channel.IsValid", "Invalid display name", "id="+o.Id)
|
||||
}
|
||||
|
||||
if len(o.Name) > 64 {
|
||||
return NewAppError("Channel.IsValid", "Invalid name", "id="+o.Id)
|
||||
}
|
||||
|
||||
if !IsValidChannelIdentifier(o.Name) {
|
||||
return NewAppError("Channel.IsValid", "Name must be 2 or more lowercase alphanumeric characters", "id="+o.Id)
|
||||
}
|
||||
|
||||
if !(o.Type == CHANNEL_OPEN || o.Type == CHANNEL_PRIVATE || o.Type == CHANNEL_DIRECT) {
|
||||
return NewAppError("Channel.IsValid", "Invalid type", "id="+o.Id)
|
||||
}
|
||||
|
||||
if len(o.Description) > 1024 {
|
||||
return NewAppError("Channel.IsValid", "Invalid description", "id="+o.Id)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *Channel) PreSave() {
|
||||
if o.Id == "" {
|
||||
o.Id = NewId()
|
||||
}
|
||||
|
||||
o.CreateAt = GetMillis()
|
||||
o.UpdateAt = o.CreateAt
|
||||
}
|
||||
|
||||
func (o *Channel) PreUpdate() {
|
||||
o.UpdateAt = GetMillis()
|
||||
}
|
||||
51
model/channel_extra.go
Обычный файл
51
model/channel_extra.go
Обычный файл
@@ -0,0 +1,51 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
)
|
||||
|
||||
type ExtraMember struct {
|
||||
Id string `json:"id"`
|
||||
FullName string `json:"full_name"`
|
||||
Email string `json:"email"`
|
||||
Roles string `json:"roles"`
|
||||
Username string `json:"username"`
|
||||
}
|
||||
|
||||
func (o *ExtraMember) Sanitize(options map[string]bool) {
|
||||
if len(options) == 0 || !options["email"] {
|
||||
o.Email = ""
|
||||
}
|
||||
if len(options) == 0 || !options["fullname"] {
|
||||
o.FullName = ""
|
||||
}
|
||||
}
|
||||
|
||||
type ChannelExtra struct {
|
||||
Id string `json:"id"`
|
||||
Members []ExtraMember `json:"members"`
|
||||
}
|
||||
|
||||
func (o *ChannelExtra) ToJson() string {
|
||||
b, err := json.Marshal(o)
|
||||
if err != nil {
|
||||
return ""
|
||||
} else {
|
||||
return string(b)
|
||||
}
|
||||
}
|
||||
|
||||
func ChannelExtraFromJson(data io.Reader) *ChannelExtra {
|
||||
decoder := json.NewDecoder(data)
|
||||
var o ChannelExtra
|
||||
err := decoder.Decode(&o)
|
||||
if err == nil {
|
||||
return &o
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
71
model/channel_list.go
Обычный файл
71
model/channel_list.go
Обычный файл
@@ -0,0 +1,71 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
)
|
||||
|
||||
type ChannelList struct {
|
||||
Channels []*Channel `json:"channels"`
|
||||
Members map[string]*ChannelMember `json:"members"`
|
||||
}
|
||||
|
||||
func (o *ChannelList) ToJson() string {
|
||||
b, err := json.Marshal(o)
|
||||
if err != nil {
|
||||
return ""
|
||||
} else {
|
||||
return string(b)
|
||||
}
|
||||
}
|
||||
|
||||
func (o *ChannelList) Etag() string {
|
||||
|
||||
id := "0"
|
||||
var t int64 = 0
|
||||
var delta int64 = 0
|
||||
|
||||
for _, v := range o.Channels {
|
||||
if v.LastPostAt > t {
|
||||
t = v.LastPostAt
|
||||
id = v.Id
|
||||
}
|
||||
|
||||
if v.UpdateAt > t {
|
||||
t = v.UpdateAt
|
||||
id = v.Id
|
||||
}
|
||||
|
||||
member := o.Members[v.Id]
|
||||
|
||||
if member != nil {
|
||||
max := v.LastPostAt
|
||||
if v.UpdateAt > max {
|
||||
max = v.UpdateAt
|
||||
}
|
||||
|
||||
delta += max - member.LastViewedAt
|
||||
|
||||
if member.LastViewedAt > t {
|
||||
t = member.LastViewedAt
|
||||
id = v.Id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Etag(id, t, delta, len(o.Channels))
|
||||
}
|
||||
|
||||
func ChannelListFromJson(data io.Reader) *ChannelList {
|
||||
decoder := json.NewDecoder(data)
|
||||
var o ChannelList
|
||||
err := decoder.Decode(&o)
|
||||
if err == nil {
|
||||
return &o
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
75
model/channel_member.go
Обычный файл
75
model/channel_member.go
Обычный файл
@@ -0,0 +1,75 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
CHANNEL_ROLE_ADMIN = "admin"
|
||||
CHANNEL_NOTIFY_ALL = "all"
|
||||
CHANNEL_NOTIFY_MENTION = "mention"
|
||||
CHANNEL_NOTIFY_NONE = "none"
|
||||
CHANNEL_NOTIFY_QUIET = "quiet"
|
||||
)
|
||||
|
||||
type ChannelMember struct {
|
||||
ChannelId string `json:"channel_id"`
|
||||
UserId string `json:"user_id"`
|
||||
Roles string `json:"roles"`
|
||||
LastViewedAt int64 `json:"last_viewed_at"`
|
||||
MsgCount int64 `json:"msg_count"`
|
||||
MentionCount int64 `json:"mention_count"`
|
||||
NotifyLevel string `json:"notify_level"`
|
||||
}
|
||||
|
||||
func (o *ChannelMember) ToJson() string {
|
||||
b, err := json.Marshal(o)
|
||||
if err != nil {
|
||||
return ""
|
||||
} else {
|
||||
return string(b)
|
||||
}
|
||||
}
|
||||
|
||||
func ChannelMemberFromJson(data io.Reader) *ChannelMember {
|
||||
decoder := json.NewDecoder(data)
|
||||
var o ChannelMember
|
||||
err := decoder.Decode(&o)
|
||||
if err == nil {
|
||||
return &o
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (o *ChannelMember) IsValid() *AppError {
|
||||
|
||||
if len(o.ChannelId) != 26 {
|
||||
return NewAppError("ChannelMember.IsValid", "Invalid channel id", "")
|
||||
}
|
||||
|
||||
if len(o.UserId) != 26 {
|
||||
return NewAppError("ChannelMember.IsValid", "Invalid user id", "")
|
||||
}
|
||||
|
||||
for _, role := range strings.Split(o.Roles, " ") {
|
||||
if !(role == "" || role == CHANNEL_ROLE_ADMIN) {
|
||||
return NewAppError("ChannelMember.IsValid", "Invalid role", "role="+role)
|
||||
}
|
||||
}
|
||||
|
||||
if len(o.NotifyLevel) > 20 || !IsChannelNotifyLevelValid(o.NotifyLevel) {
|
||||
return NewAppError("ChannelMember.IsValid", "Invalid notify level", "notify_level="+o.NotifyLevel)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func IsChannelNotifyLevelValid(notifyLevel string) bool {
|
||||
return notifyLevel == CHANNEL_NOTIFY_ALL || notifyLevel == CHANNEL_NOTIFY_MENTION || notifyLevel == CHANNEL_NOTIFY_NONE || notifyLevel == CHANNEL_NOTIFY_QUIET
|
||||
}
|
||||
60
model/channel_member_test.go
Обычный файл
60
model/channel_member_test.go
Обычный файл
@@ -0,0 +1,60 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestChannelMemberJson(t *testing.T) {
|
||||
o := ChannelMember{ChannelId: NewId(), UserId: NewId()}
|
||||
json := o.ToJson()
|
||||
ro := ChannelMemberFromJson(strings.NewReader(json))
|
||||
|
||||
if o.ChannelId != ro.ChannelId {
|
||||
t.Fatal("Ids do not match")
|
||||
}
|
||||
}
|
||||
|
||||
func TestChannelMemberIsValid(t *testing.T) {
|
||||
o := ChannelMember{}
|
||||
|
||||
if err := o.IsValid(); err == nil {
|
||||
t.Fatal("should be invalid")
|
||||
}
|
||||
|
||||
o.ChannelId = NewId()
|
||||
if err := o.IsValid(); err == nil {
|
||||
t.Fatal("should be invalid")
|
||||
}
|
||||
|
||||
o.Roles = "missing"
|
||||
o.NotifyLevel = CHANNEL_NOTIFY_ALL
|
||||
o.UserId = NewId()
|
||||
if err := o.IsValid(); err == nil {
|
||||
t.Fatal("should be invalid")
|
||||
}
|
||||
|
||||
o.Roles = CHANNEL_ROLE_ADMIN
|
||||
o.NotifyLevel = "junk"
|
||||
if err := o.IsValid(); err == nil {
|
||||
t.Fatal("should be invalid")
|
||||
}
|
||||
|
||||
o.NotifyLevel = "123456789012345678901"
|
||||
if err := o.IsValid(); err == nil {
|
||||
t.Fatal("should be invalid")
|
||||
}
|
||||
|
||||
o.NotifyLevel = CHANNEL_NOTIFY_ALL
|
||||
if err := o.IsValid(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
o.Roles = ""
|
||||
if err := o.IsValid(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
81
model/channel_test.go
Обычный файл
81
model/channel_test.go
Обычный файл
@@ -0,0 +1,81 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestChannelJson(t *testing.T) {
|
||||
o := Channel{Id: NewId(), Name: NewId()}
|
||||
json := o.ToJson()
|
||||
ro := ChannelFromJson(strings.NewReader(json))
|
||||
|
||||
if o.Id != ro.Id {
|
||||
t.Fatal("Ids do not match")
|
||||
}
|
||||
}
|
||||
|
||||
func TestChannelIsValid(t *testing.T) {
|
||||
o := Channel{}
|
||||
|
||||
if err := o.IsValid(); err == nil {
|
||||
t.Fatal("should be invalid")
|
||||
}
|
||||
|
||||
o.Id = NewId()
|
||||
if err := o.IsValid(); err == nil {
|
||||
t.Fatal("should be invalid")
|
||||
}
|
||||
|
||||
o.CreateAt = GetMillis()
|
||||
if err := o.IsValid(); err == nil {
|
||||
t.Fatal("should be invalid")
|
||||
}
|
||||
|
||||
o.UpdateAt = GetMillis()
|
||||
if err := o.IsValid(); err == nil {
|
||||
t.Fatal("should be invalid")
|
||||
}
|
||||
|
||||
o.DisplayName = strings.Repeat("01234567890", 20)
|
||||
if err := o.IsValid(); err == nil {
|
||||
t.Fatal("should be invalid")
|
||||
}
|
||||
|
||||
o.DisplayName = "1234"
|
||||
o.Name = "ZZZZZZZ"
|
||||
if err := o.IsValid(); err == nil {
|
||||
t.Fatal("should be invalid")
|
||||
}
|
||||
|
||||
o.Name = "zzzzz"
|
||||
|
||||
if err := o.IsValid(); err == nil {
|
||||
t.Fatal("should be invalid")
|
||||
}
|
||||
|
||||
o.Type = "U"
|
||||
if err := o.IsValid(); err == nil {
|
||||
t.Fatal("should be invalid")
|
||||
}
|
||||
|
||||
o.Type = "P"
|
||||
|
||||
if err := o.IsValid(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestChannelPreSave(t *testing.T) {
|
||||
o := Channel{Name: "test"}
|
||||
o.PreSave()
|
||||
o.Etag()
|
||||
}
|
||||
|
||||
func TestChannelPreUpdate(t *testing.T) {
|
||||
o := Channel{Name: "test"}
|
||||
o.PreUpdate()
|
||||
}
|
||||
652
model/client.go
Обычный файл
652
model/client.go
Обычный файл
@@ -0,0 +1,652 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
HEADER_REQUEST_ID = "X-Request-ID"
|
||||
HEADER_VERSION_ID = "X-Version-ID"
|
||||
HEADER_ETAG_SERVER = "ETag"
|
||||
HEADER_ETAG_CLIENT = "If-None-Match"
|
||||
HEADER_FORWARDED = "X-Forwarded-For"
|
||||
HEADER_FORWARDED_PROTO = "X-Forwarded-Proto"
|
||||
HEADER_TOKEN = "token"
|
||||
HEADER_AUTH = "Authorization"
|
||||
)
|
||||
|
||||
type Result struct {
|
||||
RequestId string
|
||||
Etag string
|
||||
Data interface{}
|
||||
}
|
||||
|
||||
type Client struct {
|
||||
Url string // The location of the server like "http://localhost/api/v1"
|
||||
HttpClient *http.Client // The http client
|
||||
AuthToken string
|
||||
}
|
||||
|
||||
// NewClient constructs a new client with convienence methods for talking to
|
||||
// the server.
|
||||
func NewClient(url string) *Client {
|
||||
return &Client{url, &http.Client{}, ""}
|
||||
}
|
||||
|
||||
func (c *Client) DoPost(url string, data string) (*http.Response, *AppError) {
|
||||
rq, _ := http.NewRequest("POST", c.Url+url, strings.NewReader(data))
|
||||
|
||||
if len(c.AuthToken) > 0 {
|
||||
rq.Header.Set(HEADER_AUTH, "BEARER "+c.AuthToken)
|
||||
}
|
||||
|
||||
if rp, err := c.HttpClient.Do(rq); err != nil {
|
||||
return nil, NewAppError(url, "We encountered an error while connecting to the server", err.Error())
|
||||
} else if rp.StatusCode >= 300 {
|
||||
return nil, AppErrorFromJson(rp.Body)
|
||||
} else {
|
||||
return rp, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) DoGet(url string, data string, etag string) (*http.Response, *AppError) {
|
||||
rq, _ := http.NewRequest("GET", c.Url+url, strings.NewReader(data))
|
||||
|
||||
if len(etag) > 0 {
|
||||
rq.Header.Set(HEADER_ETAG_CLIENT, etag)
|
||||
}
|
||||
|
||||
if len(c.AuthToken) > 0 {
|
||||
rq.Header.Set(HEADER_AUTH, "BEARER "+c.AuthToken)
|
||||
}
|
||||
|
||||
if rp, err := c.HttpClient.Do(rq); err != nil {
|
||||
return nil, NewAppError(url, "We encountered an error while connecting to the server", err.Error())
|
||||
} else if rp.StatusCode == 304 {
|
||||
return rp, nil
|
||||
} else if rp.StatusCode >= 300 {
|
||||
return rp, AppErrorFromJson(rp.Body)
|
||||
} else {
|
||||
return rp, nil
|
||||
}
|
||||
}
|
||||
|
||||
func getCookie(name string, resp *http.Response) *http.Cookie {
|
||||
for _, cookie := range resp.Cookies() {
|
||||
if cookie.Name == name {
|
||||
return cookie
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) Must(result *Result, err *AppError) *Result {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (c *Client) SignupTeam(email string, name string) (*Result, *AppError) {
|
||||
m := make(map[string]string)
|
||||
m["email"] = email
|
||||
m["name"] = name
|
||||
if r, err := c.DoPost("/teams/signup", MapToJson(m)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||
r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) CreateTeamFromSignup(teamSignup *TeamSignup) (*Result, *AppError) {
|
||||
if r, err := c.DoPost("/teams/create_from_signup", teamSignup.ToJson()); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||
r.Header.Get(HEADER_ETAG_SERVER), TeamSignupFromJson(r.Body)}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) CreateTeam(team *Team) (*Result, *AppError) {
|
||||
if r, err := c.DoPost("/teams/create", team.ToJson()); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||
r.Header.Get(HEADER_ETAG_SERVER), TeamFromJson(r.Body)}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) FindTeamByDomain(domain string, allServers bool) (*Result, *AppError) {
|
||||
m := make(map[string]string)
|
||||
m["domain"] = domain
|
||||
m["all"] = fmt.Sprintf("%v", allServers)
|
||||
if r, err := c.DoPost("/teams/find_team_by_domain", MapToJson(m)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
val := false
|
||||
if body, _ := ioutil.ReadAll(r.Body); string(body) == "true" {
|
||||
val = true
|
||||
}
|
||||
|
||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||
r.Header.Get(HEADER_ETAG_SERVER), val}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) FindTeams(email string) (*Result, *AppError) {
|
||||
m := make(map[string]string)
|
||||
m["email"] = email
|
||||
if r, err := c.DoPost("/teams/find_teams", MapToJson(m)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
|
||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||
r.Header.Get(HEADER_ETAG_SERVER), ArrayFromJson(r.Body)}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) FindTeamsSendEmail(email string) (*Result, *AppError) {
|
||||
m := make(map[string]string)
|
||||
m["email"] = email
|
||||
if r, err := c.DoPost("/teams/email_teams", MapToJson(m)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||
r.Header.Get(HEADER_ETAG_SERVER), ArrayFromJson(r.Body)}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) InviteMembers(invites *Invites) (*Result, *AppError) {
|
||||
if r, err := c.DoPost("/teams/invite_members", invites.ToJson()); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||
r.Header.Get(HEADER_ETAG_SERVER), InvitesFromJson(r.Body)}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) UpdateTeamName(data map[string]string) (*Result, *AppError) {
|
||||
if r, err := c.DoPost("/teams/update_name", MapToJson(data)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||
r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) CreateUser(user *User, hash string) (*Result, *AppError) {
|
||||
if r, err := c.DoPost("/users/create", user.ToJson()); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||
r.Header.Get(HEADER_ETAG_SERVER), UserFromJson(r.Body)}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) CreateUserFromSignup(user *User, data string, hash string) (*Result, *AppError) {
|
||||
if r, err := c.DoPost("/users/create?d="+data+"&h="+hash, user.ToJson()); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||
r.Header.Get(HEADER_ETAG_SERVER), UserFromJson(r.Body)}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) GetUser(id string, etag string) (*Result, *AppError) {
|
||||
if r, err := c.DoGet("/users/"+id, "", etag); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||
r.Header.Get(HEADER_ETAG_SERVER), UserFromJson(r.Body)}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) GetMe(etag string) (*Result, *AppError) {
|
||||
if r, err := c.DoGet("/users/me", "", etag); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||
r.Header.Get(HEADER_ETAG_SERVER), UserFromJson(r.Body)}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) GetProfiles(teamId string, etag string) (*Result, *AppError) {
|
||||
if r, err := c.DoGet("/users/profiles", "", etag); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||
r.Header.Get(HEADER_ETAG_SERVER), UserMapFromJson(r.Body)}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) LoginById(id string, password string) (*Result, *AppError) {
|
||||
m := make(map[string]string)
|
||||
m["id"] = id
|
||||
m["password"] = password
|
||||
return c.login(m)
|
||||
}
|
||||
|
||||
func (c *Client) LoginByEmail(domain string, email string, password string) (*Result, *AppError) {
|
||||
m := make(map[string]string)
|
||||
m["domain"] = domain
|
||||
m["email"] = email
|
||||
m["password"] = password
|
||||
return c.login(m)
|
||||
}
|
||||
|
||||
func (c *Client) LoginByEmailWithDevice(domain string, email string, password string, deviceId string) (*Result, *AppError) {
|
||||
m := make(map[string]string)
|
||||
m["domain"] = domain
|
||||
m["email"] = email
|
||||
m["password"] = password
|
||||
m["device_id"] = deviceId
|
||||
return c.login(m)
|
||||
}
|
||||
|
||||
func (c *Client) login(m map[string]string) (*Result, *AppError) {
|
||||
if r, err := c.DoPost("/users/login", MapToJson(m)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
c.AuthToken = r.Header.Get(HEADER_TOKEN)
|
||||
sessionId := getCookie(SESSION_TOKEN, r)
|
||||
|
||||
if c.AuthToken != sessionId.Value {
|
||||
NewAppError("/users/login", "Authentication tokens didn't match", "")
|
||||
}
|
||||
|
||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||
r.Header.Get(HEADER_ETAG_SERVER), UserFromJson(r.Body)}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) Logout() (*Result, *AppError) {
|
||||
if r, err := c.DoPost("/users/logout", ""); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
c.AuthToken = ""
|
||||
|
||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||
r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) RevokeSession(sessionAltId string) (*Result, *AppError) {
|
||||
m := make(map[string]string)
|
||||
m["id"] = sessionAltId
|
||||
|
||||
if r, err := c.DoPost("/users/revoke_session", MapToJson(m)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||
r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) GetSessions(id string) (*Result, *AppError) {
|
||||
if r, err := c.DoGet("/users/"+id+"/sessions", "", ""); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||
r.Header.Get(HEADER_ETAG_SERVER), SessionsFromJson(r.Body)}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) Command(channelId string, command string, suggest bool) (*Result, *AppError) {
|
||||
m := make(map[string]string)
|
||||
m["command"] = command
|
||||
m["channelId"] = channelId
|
||||
m["suggest"] = strconv.FormatBool(suggest)
|
||||
if r, err := c.DoPost("/command", MapToJson(m)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||
r.Header.Get(HEADER_ETAG_SERVER), CommandFromJson(r.Body)}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) GetAudits(id string, etag string) (*Result, *AppError) {
|
||||
if r, err := c.DoGet("/users/"+id+"/audits", "", etag); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||
r.Header.Get(HEADER_ETAG_SERVER), AuditsFromJson(r.Body)}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) CreateChannel(channel *Channel) (*Result, *AppError) {
|
||||
if r, err := c.DoPost("/channels/create", channel.ToJson()); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||
r.Header.Get(HEADER_ETAG_SERVER), ChannelFromJson(r.Body)}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) CreateDirectChannel(data map[string]string) (*Result, *AppError) {
|
||||
if r, err := c.DoPost("/channels/create_direct", MapToJson(data)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||
r.Header.Get(HEADER_ETAG_SERVER), ChannelFromJson(r.Body)}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) UpdateChannel(channel *Channel) (*Result, *AppError) {
|
||||
if r, err := c.DoPost("/channels/update", channel.ToJson()); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||
r.Header.Get(HEADER_ETAG_SERVER), ChannelFromJson(r.Body)}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) UpdateChannelDesc(data map[string]string) (*Result, *AppError) {
|
||||
if r, err := c.DoPost("/channels/update_desc", MapToJson(data)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||
r.Header.Get(HEADER_ETAG_SERVER), ChannelFromJson(r.Body)}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) UpdateNotifyLevel(data map[string]string) (*Result, *AppError) {
|
||||
if r, err := c.DoPost("/channels/update_notify_level", MapToJson(data)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||
r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) GetChannels(etag string) (*Result, *AppError) {
|
||||
if r, err := c.DoGet("/channels/", "", etag); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||
r.Header.Get(HEADER_ETAG_SERVER), ChannelListFromJson(r.Body)}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) GetMoreChannels(etag string) (*Result, *AppError) {
|
||||
if r, err := c.DoGet("/channels/more", "", etag); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||
r.Header.Get(HEADER_ETAG_SERVER), ChannelListFromJson(r.Body)}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) JoinChannel(id string) (*Result, *AppError) {
|
||||
if r, err := c.DoPost("/channels/"+id+"/join", ""); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||
r.Header.Get(HEADER_ETAG_SERVER), nil}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) LeaveChannel(id string) (*Result, *AppError) {
|
||||
if r, err := c.DoPost("/channels/"+id+"/leave", ""); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||
r.Header.Get(HEADER_ETAG_SERVER), nil}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) DeleteChannel(id string) (*Result, *AppError) {
|
||||
if r, err := c.DoPost("/channels/"+id+"/delete", ""); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||
r.Header.Get(HEADER_ETAG_SERVER), nil}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) AddChannelMember(id, user_id string) (*Result, *AppError) {
|
||||
data := make(map[string]string)
|
||||
data["user_id"] = user_id
|
||||
if r, err := c.DoPost("/channels/"+id+"/add", MapToJson(data)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||
r.Header.Get(HEADER_ETAG_SERVER), nil}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) RemoveChannelMember(id, user_id string) (*Result, *AppError) {
|
||||
data := make(map[string]string)
|
||||
data["user_id"] = user_id
|
||||
if r, err := c.DoPost("/channels/"+id+"/remove", MapToJson(data)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||
r.Header.Get(HEADER_ETAG_SERVER), nil}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) UpdateLastViewedAt(channelId string) (*Result, *AppError) {
|
||||
if r, err := c.DoPost("/channels/"+channelId+"/update_last_viewed_at", ""); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||
r.Header.Get(HEADER_ETAG_SERVER), nil}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) GetChannelExtraInfo(id string) (*Result, *AppError) {
|
||||
if r, err := c.DoGet("/channels/"+id+"/extra_info", "", ""); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||
r.Header.Get(HEADER_ETAG_SERVER), ChannelExtraFromJson(r.Body)}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) CreatePost(post *Post) (*Result, *AppError) {
|
||||
if r, err := c.DoPost("/channels/"+post.ChannelId+"/create", post.ToJson()); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||
r.Header.Get(HEADER_ETAG_SERVER), PostFromJson(r.Body)}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) CreateValetPost(post *Post) (*Result, *AppError) {
|
||||
if r, err := c.DoPost("/channels/"+post.ChannelId+"/valet_create", post.ToJson()); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||
r.Header.Get(HEADER_ETAG_SERVER), PostFromJson(r.Body)}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) UpdatePost(post *Post) (*Result, *AppError) {
|
||||
if r, err := c.DoPost("/channels/"+post.ChannelId+"/update", post.ToJson()); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||
r.Header.Get(HEADER_ETAG_SERVER), PostFromJson(r.Body)}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) GetPosts(channelId string, offset int, limit int, etag string) (*Result, *AppError) {
|
||||
if r, err := c.DoGet(fmt.Sprintf("/channels/%v/posts/%v/%v", channelId, offset, limit), "", etag); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||
r.Header.Get(HEADER_ETAG_SERVER), PostListFromJson(r.Body)}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) GetPost(channelId string, postId string, etag string) (*Result, *AppError) {
|
||||
if r, err := c.DoGet(fmt.Sprintf("/channels/%v/post/%v", channelId, postId), "", etag); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||
r.Header.Get(HEADER_ETAG_SERVER), PostListFromJson(r.Body)}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) DeletePost(channelId string, postId string) (*Result, *AppError) {
|
||||
if r, err := c.DoPost(fmt.Sprintf("/channels/%v/post/%v/delete", channelId, postId), ""); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||
r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) SearchPosts(terms string) (*Result, *AppError) {
|
||||
if r, err := c.DoGet("/posts/search?terms="+url.QueryEscape(terms), "", ""); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||
r.Header.Get(HEADER_ETAG_SERVER), PostListFromJson(r.Body)}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) UploadFile(url string, data []byte, contentType string) (*Result, *AppError) {
|
||||
rq, _ := http.NewRequest("POST", c.Url+url, bytes.NewReader(data))
|
||||
rq.Header.Set("Content-Type", contentType)
|
||||
|
||||
if len(c.AuthToken) > 0 {
|
||||
rq.Header.Set(HEADER_AUTH, "BEARER "+c.AuthToken)
|
||||
}
|
||||
|
||||
if rp, err := c.HttpClient.Do(rq); err != nil {
|
||||
return nil, NewAppError(url, "We encountered an error while connecting to the server", err.Error())
|
||||
} else if rp.StatusCode >= 300 {
|
||||
return nil, AppErrorFromJson(rp.Body)
|
||||
} else {
|
||||
return &Result{rp.Header.Get(HEADER_REQUEST_ID),
|
||||
rp.Header.Get(HEADER_ETAG_SERVER), FileUploadResponseFromJson(rp.Body)}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) GetFile(url string, isFullUrl bool) (*Result, *AppError) {
|
||||
var rq *http.Request
|
||||
if isFullUrl {
|
||||
rq, _ = http.NewRequest("GET", url, nil)
|
||||
} else {
|
||||
rq, _ = http.NewRequest("GET", c.Url+url, nil)
|
||||
}
|
||||
|
||||
if len(c.AuthToken) > 0 {
|
||||
rq.Header.Set(HEADER_AUTH, "BEARER "+c.AuthToken)
|
||||
}
|
||||
|
||||
if rp, err := c.HttpClient.Do(rq); err != nil {
|
||||
return nil, NewAppError(url, "We encountered an error while connecting to the server", err.Error())
|
||||
} else if rp.StatusCode >= 300 {
|
||||
return nil, AppErrorFromJson(rp.Body)
|
||||
} else {
|
||||
return &Result{rp.Header.Get(HEADER_REQUEST_ID),
|
||||
rp.Header.Get(HEADER_ETAG_SERVER), rp.Body}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) GetPublicLink(data map[string]string) (*Result, *AppError) {
|
||||
if r, err := c.DoPost("/files/get_public_link", MapToJson(data)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||
r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) UpdateUser(user *User) (*Result, *AppError) {
|
||||
if r, err := c.DoPost("/users/update", user.ToJson()); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||
r.Header.Get(HEADER_ETAG_SERVER), UserFromJson(r.Body)}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) UpdateUserRoles(data map[string]string) (*Result, *AppError) {
|
||||
if r, err := c.DoPost("/users/update_roles", MapToJson(data)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||
r.Header.Get(HEADER_ETAG_SERVER), UserFromJson(r.Body)}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) UpdateActive(userId string, active bool) (*Result, *AppError) {
|
||||
data := make(map[string]string)
|
||||
data["user_id"] = userId
|
||||
data["active"] = strconv.FormatBool(active)
|
||||
if r, err := c.DoPost("/users/update_active", MapToJson(data)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||
r.Header.Get(HEADER_ETAG_SERVER), UserFromJson(r.Body)}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) UpdateUserNotify(data map[string]string) (*Result, *AppError) {
|
||||
if r, err := c.DoPost("/users/update_notify", MapToJson(data)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||
r.Header.Get(HEADER_ETAG_SERVER), UserFromJson(r.Body)}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) UpdateUserPassword(userId, currentPassword, newPassword string) (*Result, *AppError) {
|
||||
data := make(map[string]string)
|
||||
data["current_password"] = currentPassword
|
||||
data["new_password"] = newPassword
|
||||
data["user_id"] = userId
|
||||
|
||||
if r, err := c.DoPost("/users/newpassword", MapToJson(data)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||
r.Header.Get(HEADER_ETAG_SERVER), UserFromJson(r.Body)}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) SendPasswordReset(data map[string]string) (*Result, *AppError) {
|
||||
if r, err := c.DoPost("/users/send_password_reset", MapToJson(data)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||
r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) ResetPassword(data map[string]string) (*Result, *AppError) {
|
||||
if r, err := c.DoPost("/users/reset_password", MapToJson(data)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||
r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) GetStatuses() (*Result, *AppError) {
|
||||
if r, err := c.DoGet("/users/status", "", ""); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||
r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) MockSession(sessionToken string) {
|
||||
c.AuthToken = sessionToken
|
||||
}
|
||||
53
model/command.go
Обычный файл
53
model/command.go
Обычный файл
@@ -0,0 +1,53 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
)
|
||||
|
||||
const (
|
||||
RESP_EXECUTED = "executed"
|
||||
)
|
||||
|
||||
type Command struct {
|
||||
Command string `json:"command"`
|
||||
Response string `json:"reponse"`
|
||||
GotoLocation string `json:"goto_location"`
|
||||
ChannelId string `json:"channel_id"`
|
||||
Suggest bool `json:"-"`
|
||||
Suggestions []*SuggestCommand `json:"suggestions"`
|
||||
}
|
||||
|
||||
func (o *Command) AddSuggestion(suggest *SuggestCommand) {
|
||||
|
||||
if o.Suggest {
|
||||
if o.Suggestions == nil {
|
||||
o.Suggestions = make([]*SuggestCommand, 0, 128)
|
||||
}
|
||||
|
||||
o.Suggestions = append(o.Suggestions, suggest)
|
||||
}
|
||||
}
|
||||
|
||||
func (o *Command) ToJson() string {
|
||||
b, err := json.Marshal(o)
|
||||
if err != nil {
|
||||
return ""
|
||||
} else {
|
||||
return string(b)
|
||||
}
|
||||
}
|
||||
|
||||
func CommandFromJson(data io.Reader) *Command {
|
||||
decoder := json.NewDecoder(data)
|
||||
var o Command
|
||||
err := decoder.Decode(&o)
|
||||
if err == nil {
|
||||
return &o
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
25
model/command_test.go
Обычный файл
25
model/command_test.go
Обычный файл
@@ -0,0 +1,25 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCommandJson(t *testing.T) {
|
||||
|
||||
command := &Command{Command: NewId(), Suggest: true}
|
||||
command.AddSuggestion(&SuggestCommand{Suggestion: NewId()})
|
||||
json := command.ToJson()
|
||||
result := CommandFromJson(strings.NewReader(json))
|
||||
|
||||
if command.Command != result.Command {
|
||||
t.Fatal("Ids do not match")
|
||||
}
|
||||
|
||||
if command.Suggestions[0].Suggestion != result.Suggestions[0].Suggestion {
|
||||
t.Fatal("Ids do not match")
|
||||
}
|
||||
}
|
||||
42
model/file.go
Обычный файл
42
model/file.go
Обычный файл
@@ -0,0 +1,42 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
)
|
||||
|
||||
const (
|
||||
MAX_FILE_SIZE = 50000000 // 50 MB
|
||||
)
|
||||
|
||||
var (
|
||||
IMAGE_EXTENSIONS = [4]string{".jpg", ".gif", ".bmp", ".png"}
|
||||
IMAGE_MIME_TYPES = map[string]string{".jpg": "image/jpeg", ".gif": "image/gif", ".bmp": "image/bmp", ".png": "image/png", ".tiff": "image/tiff"}
|
||||
)
|
||||
|
||||
type FileUploadResponse struct {
|
||||
Filenames []string `json:"filenames"`
|
||||
}
|
||||
|
||||
func FileUploadResponseFromJson(data io.Reader) *FileUploadResponse {
|
||||
decoder := json.NewDecoder(data)
|
||||
var o FileUploadResponse
|
||||
err := decoder.Decode(&o)
|
||||
if err == nil {
|
||||
return &o
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (o *FileUploadResponse) ToJson() string {
|
||||
b, err := json.Marshal(o)
|
||||
if err != nil {
|
||||
return ""
|
||||
} else {
|
||||
return string(b)
|
||||
}
|
||||
}
|
||||
54
model/message.go
Обычный файл
54
model/message.go
Обычный файл
@@ -0,0 +1,54 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
)
|
||||
|
||||
const (
|
||||
ACTION_TYPING = "typing"
|
||||
ACTION_POSTED = "posted"
|
||||
ACTION_POST_EDITED = "post_edited"
|
||||
ACTION_POST_DELETED = "post_deleted"
|
||||
ACTION_VIEWED = "viewed"
|
||||
ACTION_NEW_USER = "new_user"
|
||||
)
|
||||
|
||||
type Message struct {
|
||||
TeamId string `json:"team_id"`
|
||||
ChannelId string `json:"channel_id"`
|
||||
UserId string `json:"user_id"`
|
||||
Action string `json:"action"`
|
||||
Props map[string]string `json:"props"`
|
||||
}
|
||||
|
||||
func (m *Message) Add(key string, value string) {
|
||||
m.Props[key] = value
|
||||
}
|
||||
|
||||
func NewMessage(teamId string, channekId string, userId string, action string) *Message {
|
||||
return &Message{TeamId: teamId, ChannelId: channekId, UserId: userId, Action: action, Props: make(map[string]string)}
|
||||
}
|
||||
|
||||
func (o *Message) ToJson() string {
|
||||
b, err := json.Marshal(o)
|
||||
if err != nil {
|
||||
return ""
|
||||
} else {
|
||||
return string(b)
|
||||
}
|
||||
}
|
||||
|
||||
func MessageFromJson(data io.Reader) *Message {
|
||||
decoder := json.NewDecoder(data)
|
||||
var o Message
|
||||
err := decoder.Decode(&o)
|
||||
if err == nil {
|
||||
return &o
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
24
model/message_test.go
Обычный файл
24
model/message_test.go
Обычный файл
@@ -0,0 +1,24 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMessgaeJson(t *testing.T) {
|
||||
m := NewMessage(NewId(), NewId(), NewId(), ACTION_TYPING)
|
||||
m.Add("RootId", NewId())
|
||||
json := m.ToJson()
|
||||
result := MessageFromJson(strings.NewReader(json))
|
||||
|
||||
if m.TeamId != result.TeamId {
|
||||
t.Fatal("Ids do not match")
|
||||
}
|
||||
|
||||
if m.Props["RootId"] != result.Props["RootId"] {
|
||||
t.Fatal("Ids do not match")
|
||||
}
|
||||
}
|
||||
147
model/post.go
Обычный файл
147
model/post.go
Обычный файл
@@ -0,0 +1,147 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
)
|
||||
|
||||
const (
|
||||
POST_DEFAULT = ""
|
||||
)
|
||||
|
||||
type Post struct {
|
||||
Id string `json:"id"`
|
||||
CreateAt int64 `json:"create_at"`
|
||||
UpdateAt int64 `json:"update_at"`
|
||||
DeleteAt int64 `json:"delete_at"`
|
||||
UserId string `json:"user_id"`
|
||||
ChannelId string `json:"channel_id"`
|
||||
RootId string `json:"root_id"`
|
||||
ParentId string `json:"parent_id"`
|
||||
OriginalId string `json:"original_id"`
|
||||
Message string `json:"message"`
|
||||
ImgCount int64 `json:"img_count"`
|
||||
Type string `json:"type"`
|
||||
Props StringMap `json:"props"`
|
||||
Hashtags string `json:"hashtags"`
|
||||
Filenames StringArray `json:"filenames"`
|
||||
}
|
||||
|
||||
func (o *Post) ToJson() string {
|
||||
b, err := json.Marshal(o)
|
||||
if err != nil {
|
||||
return ""
|
||||
} else {
|
||||
return string(b)
|
||||
}
|
||||
}
|
||||
|
||||
func PostFromJson(data io.Reader) *Post {
|
||||
decoder := json.NewDecoder(data)
|
||||
var o Post
|
||||
err := decoder.Decode(&o)
|
||||
if err == nil {
|
||||
return &o
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (o *Post) Etag() string {
|
||||
return Etag(o.Id, o.UpdateAt)
|
||||
}
|
||||
|
||||
func (o *Post) IsValid() *AppError {
|
||||
|
||||
if len(o.Id) != 26 {
|
||||
return NewAppError("Post.IsValid", "Invalid Id", "")
|
||||
}
|
||||
|
||||
if o.CreateAt == 0 {
|
||||
return NewAppError("Post.IsValid", "Create at must be a valid time", "id="+o.Id)
|
||||
}
|
||||
|
||||
if o.UpdateAt == 0 {
|
||||
return NewAppError("Post.IsValid", "Update at must be a valid time", "id="+o.Id)
|
||||
}
|
||||
|
||||
if len(o.UserId) != 26 {
|
||||
return NewAppError("Post.IsValid", "Invalid user id", "")
|
||||
}
|
||||
|
||||
if len(o.ChannelId) != 26 {
|
||||
return NewAppError("Post.IsValid", "Invalid channel id", "")
|
||||
}
|
||||
|
||||
if !(len(o.RootId) == 26 || len(o.RootId) == 0) {
|
||||
return NewAppError("Post.IsValid", "Invalid root id", "")
|
||||
}
|
||||
|
||||
if !(len(o.ParentId) == 26 || len(o.ParentId) == 0) {
|
||||
return NewAppError("Post.IsValid", "Invalid parent id", "")
|
||||
}
|
||||
|
||||
if len(o.ParentId) == 26 && len(o.RootId) == 0 {
|
||||
return NewAppError("Post.IsValid", "Invalid root id must be set if parent id set", "")
|
||||
}
|
||||
|
||||
if !(len(o.OriginalId) == 26 || len(o.OriginalId) == 0) {
|
||||
return NewAppError("Post.IsValid", "Invalid original id", "")
|
||||
}
|
||||
|
||||
if len(o.Message) > 4000 {
|
||||
return NewAppError("Post.IsValid", "Invalid message", "id="+o.Id)
|
||||
}
|
||||
|
||||
if len(o.Hashtags) > 1000 {
|
||||
return NewAppError("Post.IsValid", "Invalid hashtags", "id="+o.Id)
|
||||
}
|
||||
|
||||
if !(o.Type == POST_DEFAULT) {
|
||||
return NewAppError("Post.IsValid", "Invalid type", "id="+o.Type)
|
||||
}
|
||||
|
||||
if len(ArrayToJson(o.Filenames)) > 4000 {
|
||||
return NewAppError("Post.IsValid", "Invalid filenames", "id="+o.Id)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *Post) PreSave() {
|
||||
if o.Id == "" {
|
||||
o.Id = NewId()
|
||||
}
|
||||
|
||||
o.OriginalId = ""
|
||||
|
||||
o.CreateAt = GetMillis()
|
||||
o.UpdateAt = o.CreateAt
|
||||
|
||||
if o.Props == nil {
|
||||
o.Props = make(map[string]string)
|
||||
}
|
||||
|
||||
if o.Filenames == nil {
|
||||
o.Filenames = []string{}
|
||||
}
|
||||
}
|
||||
|
||||
func (o *Post) MakeNonNil() {
|
||||
if o.Props == nil {
|
||||
o.Props = make(map[string]string)
|
||||
}
|
||||
if o.Filenames == nil {
|
||||
o.Filenames = []string{}
|
||||
}
|
||||
}
|
||||
|
||||
func (o *Post) AddProp(key string, value string) {
|
||||
|
||||
o.MakeNonNil()
|
||||
|
||||
o.Props[key] = value
|
||||
}
|
||||
91
model/post_list.go
Обычный файл
91
model/post_list.go
Обычный файл
@@ -0,0 +1,91 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
)
|
||||
|
||||
type PostList struct {
|
||||
Order []string `json:"order"`
|
||||
Posts map[string]*Post `json:"posts"`
|
||||
}
|
||||
|
||||
func (o *PostList) ToJson() string {
|
||||
b, err := json.Marshal(o)
|
||||
if err != nil {
|
||||
return ""
|
||||
} else {
|
||||
return string(b)
|
||||
}
|
||||
}
|
||||
|
||||
func (o *PostList) MakeNonNil() {
|
||||
if o.Order == nil {
|
||||
o.Order = make([]string, 0)
|
||||
}
|
||||
|
||||
if o.Posts == nil {
|
||||
o.Posts = make(map[string]*Post)
|
||||
}
|
||||
|
||||
for _, v := range o.Posts {
|
||||
v.MakeNonNil()
|
||||
}
|
||||
}
|
||||
|
||||
func (o *PostList) AddOrder(id string) {
|
||||
|
||||
if o.Order == nil {
|
||||
o.Order = make([]string, 0, 128)
|
||||
}
|
||||
|
||||
o.Order = append(o.Order, id)
|
||||
}
|
||||
|
||||
func (o *PostList) AddPost(post *Post) {
|
||||
|
||||
if o.Posts == nil {
|
||||
o.Posts = make(map[string]*Post)
|
||||
}
|
||||
|
||||
o.Posts[post.Id] = post
|
||||
}
|
||||
|
||||
func (o *PostList) Etag() string {
|
||||
|
||||
id := "0"
|
||||
var t int64 = 0
|
||||
|
||||
for _, v := range o.Posts {
|
||||
if v.UpdateAt > t {
|
||||
t = v.UpdateAt
|
||||
id = v.Id
|
||||
}
|
||||
}
|
||||
|
||||
return Etag(id, t)
|
||||
}
|
||||
|
||||
func (o *PostList) IsChannelId(channelId string) bool {
|
||||
for _, v := range o.Posts {
|
||||
if v.ChannelId != channelId {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func PostListFromJson(data io.Reader) *PostList {
|
||||
decoder := json.NewDecoder(data)
|
||||
var o PostList
|
||||
err := decoder.Decode(&o)
|
||||
if err == nil {
|
||||
return &o
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
36
model/post_list_test.go
Обычный файл
36
model/post_list_test.go
Обычный файл
@@ -0,0 +1,36 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPostListJson(t *testing.T) {
|
||||
|
||||
pl := PostList{}
|
||||
p1 := &Post{Id: NewId(), Message: NewId()}
|
||||
pl.AddPost(p1)
|
||||
p2 := &Post{Id: NewId(), Message: NewId()}
|
||||
pl.AddPost(p2)
|
||||
|
||||
pl.AddOrder(p1.Id)
|
||||
pl.AddOrder(p2.Id)
|
||||
|
||||
json := pl.ToJson()
|
||||
rpl := PostListFromJson(strings.NewReader(json))
|
||||
|
||||
if rpl.Posts[p1.Id].Message != p1.Message {
|
||||
t.Fatal("failed to serialize")
|
||||
}
|
||||
|
||||
if rpl.Posts[p2.Id].Message != p2.Message {
|
||||
t.Fatal("failed to serialize")
|
||||
}
|
||||
|
||||
if rpl.Order[1] != p2.Id {
|
||||
t.Fatal("failed to serialize")
|
||||
}
|
||||
}
|
||||
87
model/post_test.go
Обычный файл
87
model/post_test.go
Обычный файл
@@ -0,0 +1,87 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPostJson(t *testing.T) {
|
||||
o := Post{Id: NewId(), Message: NewId()}
|
||||
json := o.ToJson()
|
||||
ro := PostFromJson(strings.NewReader(json))
|
||||
|
||||
if o.Id != ro.Id {
|
||||
t.Fatal("Ids do not match")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPostIsValid(t *testing.T) {
|
||||
o := Post{}
|
||||
|
||||
if err := o.IsValid(); err == nil {
|
||||
t.Fatal("should be invalid")
|
||||
}
|
||||
|
||||
o.Id = NewId()
|
||||
if err := o.IsValid(); err == nil {
|
||||
t.Fatal("should be invalid")
|
||||
}
|
||||
|
||||
o.CreateAt = GetMillis()
|
||||
if err := o.IsValid(); err == nil {
|
||||
t.Fatal("should be invalid")
|
||||
}
|
||||
|
||||
o.UpdateAt = GetMillis()
|
||||
if err := o.IsValid(); err == nil {
|
||||
t.Fatal("should be invalid")
|
||||
}
|
||||
|
||||
o.UserId = NewId()
|
||||
if err := o.IsValid(); err == nil {
|
||||
t.Fatal("should be invalid")
|
||||
}
|
||||
|
||||
o.ChannelId = NewId()
|
||||
o.RootId = "123"
|
||||
if err := o.IsValid(); err == nil {
|
||||
t.Fatal("should be invalid")
|
||||
}
|
||||
|
||||
o.RootId = ""
|
||||
o.ParentId = "123"
|
||||
if err := o.IsValid(); err == nil {
|
||||
t.Fatal("should be invalid")
|
||||
}
|
||||
|
||||
o.ParentId = NewId()
|
||||
o.RootId = ""
|
||||
if err := o.IsValid(); err == nil {
|
||||
t.Fatal("should be invalid")
|
||||
}
|
||||
|
||||
o.ParentId = ""
|
||||
o.Message = strings.Repeat("0", 4001)
|
||||
if err := o.IsValid(); err == nil {
|
||||
t.Fatal("should be invalid")
|
||||
}
|
||||
|
||||
o.Message = strings.Repeat("0", 4000)
|
||||
if err := o.IsValid(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
o.Message = "test"
|
||||
if err := o.IsValid(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPostPreSave(t *testing.T) {
|
||||
o := Post{Message: "test"}
|
||||
o.PreSave()
|
||||
o.Etag()
|
||||
}
|
||||
119
model/session.go
Обычный файл
119
model/session.go
Обычный файл
@@ -0,0 +1,119 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
)
|
||||
|
||||
const (
|
||||
SESSION_TOKEN = "MMSID"
|
||||
SESSION_TIME_WEB_IN_DAYS = 365
|
||||
SESSION_TIME_WEB_IN_SECS = 60 * 60 * 24 * SESSION_TIME_WEB_IN_DAYS
|
||||
SESSION_TIME_MOBILE_IN_DAYS = 365
|
||||
SESSION_TIME_MOBILE_IN_SECS = 60 * 60 * 24 * SESSION_TIME_MOBILE_IN_DAYS
|
||||
SESSION_CACHE_IN_SECS = 60 * 10
|
||||
SESSION_CACHE_SIZE = 10000
|
||||
SESSION_PROP_PLATFORM = "platform"
|
||||
SESSION_PROP_OS = "os"
|
||||
SESSION_PROP_BROWSER = "browser"
|
||||
)
|
||||
|
||||
type Session struct {
|
||||
Id string `json:"id"`
|
||||
AltId string `json:"alt_id"`
|
||||
CreateAt int64 `json:"create_at"`
|
||||
ExpiresAt int64 `json:"expires_at"`
|
||||
LastActivityAt int64 `json:"last_activity_at"`
|
||||
UserId string `json:"user_id"`
|
||||
TeamId string `json:"team_id"`
|
||||
DeviceId string `json:"device_id"`
|
||||
Roles string `json:"roles"`
|
||||
Props StringMap `json:"props"`
|
||||
}
|
||||
|
||||
func (me *Session) ToJson() string {
|
||||
b, err := json.Marshal(me)
|
||||
if err != nil {
|
||||
return ""
|
||||
} else {
|
||||
return string(b)
|
||||
}
|
||||
}
|
||||
|
||||
func SessionFromJson(data io.Reader) *Session {
|
||||
decoder := json.NewDecoder(data)
|
||||
var me Session
|
||||
err := decoder.Decode(&me)
|
||||
if err == nil {
|
||||
return &me
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (me *Session) PreSave() {
|
||||
if me.Id == "" {
|
||||
me.Id = NewId()
|
||||
}
|
||||
|
||||
me.AltId = NewId()
|
||||
|
||||
me.CreateAt = GetMillis()
|
||||
me.LastActivityAt = me.CreateAt
|
||||
|
||||
if me.Props == nil {
|
||||
me.Props = make(map[string]string)
|
||||
}
|
||||
}
|
||||
|
||||
func (me *Session) Sanitize() {
|
||||
me.Id = ""
|
||||
}
|
||||
|
||||
func (me *Session) IsExpired() bool {
|
||||
|
||||
if me.ExpiresAt <= 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
if GetMillis() > me.ExpiresAt {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (me *Session) SetExpireInDays(days int64) {
|
||||
me.ExpiresAt = GetMillis() + (1000 * 60 * 60 * 24 * days)
|
||||
}
|
||||
|
||||
func (me *Session) AddProp(key string, value string) {
|
||||
|
||||
if me.Props == nil {
|
||||
me.Props = make(map[string]string)
|
||||
}
|
||||
|
||||
me.Props[key] = value
|
||||
}
|
||||
|
||||
func SessionsToJson(o []*Session) string {
|
||||
if b, err := json.Marshal(o); err != nil {
|
||||
return "[]"
|
||||
} else {
|
||||
return string(b)
|
||||
}
|
||||
}
|
||||
|
||||
func SessionsFromJson(data io.Reader) []*Session {
|
||||
decoder := json.NewDecoder(data)
|
||||
var o []*Session
|
||||
err := decoder.Decode(&o)
|
||||
if err == nil {
|
||||
return o
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
35
model/session_test.go
Обычный файл
35
model/session_test.go
Обычный файл
@@ -0,0 +1,35 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestSessionJson(t *testing.T) {
|
||||
session := Session{}
|
||||
session.PreSave()
|
||||
json := session.ToJson()
|
||||
rsession := SessionFromJson(strings.NewReader(json))
|
||||
|
||||
if rsession.Id != session.Id {
|
||||
t.Fatal("Ids do not match")
|
||||
}
|
||||
|
||||
session.Sanitize()
|
||||
|
||||
if session.IsExpired() {
|
||||
t.Fatal("Shouldn't expire")
|
||||
}
|
||||
|
||||
session.ExpiresAt = GetMillis()
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
if !session.IsExpired() {
|
||||
t.Fatal("Should expire")
|
||||
}
|
||||
|
||||
session.SetExpireInDays(10)
|
||||
}
|
||||
34
model/suggest_command.go
Обычный файл
34
model/suggest_command.go
Обычный файл
@@ -0,0 +1,34 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
)
|
||||
|
||||
type SuggestCommand struct {
|
||||
Suggestion string `json:"suggestion"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
func (o *SuggestCommand) ToJson() string {
|
||||
b, err := json.Marshal(o)
|
||||
if err != nil {
|
||||
return ""
|
||||
} else {
|
||||
return string(b)
|
||||
}
|
||||
}
|
||||
|
||||
func SuggestCommandFromJson(data io.Reader) *SuggestCommand {
|
||||
decoder := json.NewDecoder(data)
|
||||
var o SuggestCommand
|
||||
err := decoder.Decode(&o)
|
||||
if err == nil {
|
||||
return &o
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
19
model/suggest_command_test.go
Обычный файл
19
model/suggest_command_test.go
Обычный файл
@@ -0,0 +1,19 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSuggestCommandJson(t *testing.T) {
|
||||
command := &SuggestCommand{Suggestion: NewId()}
|
||||
json := command.ToJson()
|
||||
result := SuggestCommandFromJson(strings.NewReader(json))
|
||||
|
||||
if command.Suggestion != result.Suggestion {
|
||||
t.Fatal("Ids do not match")
|
||||
}
|
||||
}
|
||||
141
model/team.go
Обычный файл
141
model/team.go
Обычный файл
@@ -0,0 +1,141 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
)
|
||||
|
||||
const (
|
||||
TEAM_OPEN = "O"
|
||||
TEAM_INVITE = "I"
|
||||
)
|
||||
|
||||
type Team struct {
|
||||
Id string `json:"id"`
|
||||
CreateAt int64 `json:"create_at"`
|
||||
UpdateAt int64 `json:"update_at"`
|
||||
DeleteAt int64 `json:"delete_at"`
|
||||
Name string `json:"name"`
|
||||
Domain string `json:"domain"`
|
||||
Email string `json:"email"`
|
||||
Type string `json:"type"`
|
||||
CompanyName string `json:"company_name"`
|
||||
AllowedDomains string `json:"allowed_domains"`
|
||||
}
|
||||
|
||||
type Invites struct {
|
||||
Invites []map[string]string `json:"invites"`
|
||||
}
|
||||
|
||||
func InvitesFromJson(data io.Reader) *Invites {
|
||||
decoder := json.NewDecoder(data)
|
||||
var o Invites
|
||||
err := decoder.Decode(&o)
|
||||
if err == nil {
|
||||
return &o
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (o *Invites) ToJson() string {
|
||||
b, err := json.Marshal(o)
|
||||
if err != nil {
|
||||
return ""
|
||||
} else {
|
||||
return string(b)
|
||||
}
|
||||
}
|
||||
|
||||
func (o *Team) ToJson() string {
|
||||
b, err := json.Marshal(o)
|
||||
if err != nil {
|
||||
return ""
|
||||
} else {
|
||||
return string(b)
|
||||
}
|
||||
}
|
||||
|
||||
func TeamFromJson(data io.Reader) *Team {
|
||||
decoder := json.NewDecoder(data)
|
||||
var o Team
|
||||
err := decoder.Decode(&o)
|
||||
if err == nil {
|
||||
return &o
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (o *Team) Etag() string {
|
||||
return Etag(o.Id, o.UpdateAt)
|
||||
}
|
||||
|
||||
func (o *Team) IsValid() *AppError {
|
||||
|
||||
if len(o.Id) != 26 {
|
||||
return NewAppError("Team.IsValid", "Invalid Id", "")
|
||||
}
|
||||
|
||||
if o.CreateAt == 0 {
|
||||
return NewAppError("Team.IsValid", "Create at must be a valid time", "id="+o.Id)
|
||||
}
|
||||
|
||||
if o.UpdateAt == 0 {
|
||||
return NewAppError("Team.IsValid", "Update at must be a valid time", "id="+o.Id)
|
||||
}
|
||||
|
||||
if len(o.Email) > 128 {
|
||||
return NewAppError("Team.IsValid", "Invalid email", "id="+o.Id)
|
||||
}
|
||||
|
||||
if !IsValidEmail(o.Email) {
|
||||
return NewAppError("Team.IsValid", "Invalid email", "id="+o.Id)
|
||||
}
|
||||
|
||||
if len(o.Name) > 64 {
|
||||
return NewAppError("Team.IsValid", "Invalid name", "id="+o.Id)
|
||||
}
|
||||
|
||||
if len(o.Domain) > 64 {
|
||||
return NewAppError("Team.IsValid", "Invalid domain", "id="+o.Id)
|
||||
}
|
||||
|
||||
if IsReservedDomain(o.Domain) {
|
||||
return NewAppError("Team.IsValid", "This URL is unavailable. Please try another.", "id="+o.Id)
|
||||
}
|
||||
|
||||
if !IsValidDomain(o.Domain) {
|
||||
return NewAppError("Team.IsValid", "Domain must be 4 or more lowercase alphanumeric characters", "id="+o.Id)
|
||||
}
|
||||
|
||||
if !(o.Type == TEAM_OPEN || o.Type == TEAM_INVITE) {
|
||||
return NewAppError("Team.IsValid", "Invalid type", "id="+o.Id)
|
||||
}
|
||||
|
||||
if len(o.CompanyName) > 64 {
|
||||
return NewAppError("Team.IsValid", "Invalid company name", "id="+o.Id)
|
||||
}
|
||||
|
||||
if len(o.AllowedDomains) > 500 {
|
||||
return NewAppError("Team.IsValid", "Invalid allowed domains", "id="+o.Id)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *Team) PreSave() {
|
||||
if o.Id == "" {
|
||||
o.Id = NewId()
|
||||
}
|
||||
|
||||
o.CreateAt = GetMillis()
|
||||
o.UpdateAt = o.CreateAt
|
||||
}
|
||||
|
||||
func (o *Team) PreUpdate() {
|
||||
o.UpdateAt = GetMillis()
|
||||
}
|
||||
40
model/team_signup.go
Обычный файл
40
model/team_signup.go
Обычный файл
@@ -0,0 +1,40 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
type TeamSignup struct {
|
||||
Team Team `json:"team"`
|
||||
User User `json:"user"`
|
||||
Invites []string `json:"invites"`
|
||||
Data string `json:"data"`
|
||||
Hash string `json:"hash"`
|
||||
}
|
||||
|
||||
func TeamSignupFromJson(data io.Reader) *TeamSignup {
|
||||
decoder := json.NewDecoder(data)
|
||||
var o TeamSignup
|
||||
err := decoder.Decode(&o)
|
||||
if err == nil {
|
||||
return &o
|
||||
} else {
|
||||
fmt.Println(err)
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (o *TeamSignup) ToJson() string {
|
||||
b, err := json.Marshal(o)
|
||||
if err != nil {
|
||||
return ""
|
||||
} else {
|
||||
return string(b)
|
||||
}
|
||||
}
|
||||
20
model/team_signup_test.go
Обычный файл
20
model/team_signup_test.go
Обычный файл
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestTeamSignupJson(t *testing.T) {
|
||||
team := Team{Id: NewId(), Name: NewId()}
|
||||
o := TeamSignup{Team: team, Data: "data"}
|
||||
json := o.ToJson()
|
||||
ro := TeamSignupFromJson(strings.NewReader(json))
|
||||
|
||||
if o.Team.Id != ro.Team.Id {
|
||||
t.Fatal("Ids do not match")
|
||||
}
|
||||
}
|
||||
76
model/team_test.go
Обычный файл
76
model/team_test.go
Обычный файл
@@ -0,0 +1,76 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestTeamJson(t *testing.T) {
|
||||
o := Team{Id: NewId(), Name: NewId()}
|
||||
json := o.ToJson()
|
||||
ro := TeamFromJson(strings.NewReader(json))
|
||||
|
||||
if o.Id != ro.Id {
|
||||
t.Fatal("Ids do not match")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTeamIsValid(t *testing.T) {
|
||||
o := Team{}
|
||||
|
||||
if err := o.IsValid(); err == nil {
|
||||
t.Fatal("should be invalid")
|
||||
}
|
||||
|
||||
o.Id = NewId()
|
||||
if err := o.IsValid(); err == nil {
|
||||
t.Fatal("should be invalid")
|
||||
}
|
||||
|
||||
o.CreateAt = GetMillis()
|
||||
if err := o.IsValid(); err == nil {
|
||||
t.Fatal("should be invalid")
|
||||
}
|
||||
|
||||
o.UpdateAt = GetMillis()
|
||||
if err := o.IsValid(); err == nil {
|
||||
t.Fatal("should be invalid")
|
||||
}
|
||||
|
||||
o.Email = strings.Repeat("01234567890", 20)
|
||||
if err := o.IsValid(); err == nil {
|
||||
t.Fatal("should be invalid")
|
||||
}
|
||||
|
||||
o.Email = "corey@hulen.com"
|
||||
o.Name = strings.Repeat("01234567890", 20)
|
||||
if err := o.IsValid(); err == nil {
|
||||
t.Fatal("should be invalid")
|
||||
}
|
||||
|
||||
o.Name = "1234"
|
||||
o.Domain = "ZZZZZZZ"
|
||||
if err := o.IsValid(); err == nil {
|
||||
t.Fatal("should be invalid")
|
||||
}
|
||||
|
||||
o.Domain = "zzzzz"
|
||||
o.Type = TEAM_OPEN
|
||||
if err := o.IsValid(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTeamPreSave(t *testing.T) {
|
||||
o := Team{Name: "test"}
|
||||
o.PreSave()
|
||||
o.Etag()
|
||||
}
|
||||
|
||||
func TestTeamPreUpdate(t *testing.T) {
|
||||
o := Team{Name: "test"}
|
||||
o.PreUpdate()
|
||||
}
|
||||
293
model/user.go
Обычный файл
293
model/user.go
Обычный файл
@@ -0,0 +1,293 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"code.google.com/p/go.crypto/bcrypt"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
ROLE_ADMIN = "admin"
|
||||
ROLE_SYSTEM_ADMIN = "system_admin"
|
||||
ROLE_SYSTEM_SUPPORT = "system_support"
|
||||
USER_AWAY_TIMEOUT = 5 * 60 * 1000 // 5 minutes
|
||||
USER_OFFLINE_TIMEOUT = 5 * 60 * 1000 // 5 minutes
|
||||
USER_OFFLINE = "offline"
|
||||
USER_AWAY = "away"
|
||||
USER_ONLINE = "online"
|
||||
USER_NOTIFY_ALL = "all"
|
||||
USER_NOTIFY_MENTION = "mention"
|
||||
USER_NOTIFY_NONE = "none"
|
||||
BOT_USERNAME = "valet"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
Id string `json:"id"`
|
||||
CreateAt int64 `json:"create_at"`
|
||||
UpdateAt int64 `json:"update_at"`
|
||||
DeleteAt int64 `json:"delete_at"`
|
||||
TeamId string `json:"team_id"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
AuthData string `json:"auth_data"`
|
||||
Email string `json:"email"`
|
||||
EmailVerified bool `json:"email_verified"`
|
||||
FullName string `json:"full_name"`
|
||||
Roles string `json:"roles"`
|
||||
LastActivityAt int64 `json:"last_activity_at"`
|
||||
LastPingAt int64 `json:"last_ping_at"`
|
||||
AllowMarketing bool `json:"allow_marketing"`
|
||||
Props StringMap `json:"props"`
|
||||
NotifyProps StringMap `json:"notify_props"`
|
||||
LastPasswordUpdate int64 `json:"last_password_update"`
|
||||
}
|
||||
|
||||
// IsValid validates the user and returns an error if it isn't configured
|
||||
// correctly.
|
||||
func (u *User) IsValid() *AppError {
|
||||
|
||||
if len(u.Id) != 26 {
|
||||
return NewAppError("User.IsValid", "Invalid user id", "")
|
||||
}
|
||||
|
||||
if u.CreateAt == 0 {
|
||||
return NewAppError("User.IsValid", "Create at must be a valid time", "user_id="+u.Id)
|
||||
}
|
||||
|
||||
if u.UpdateAt == 0 {
|
||||
return NewAppError("User.IsValid", "Update at must be a valid time", "user_id="+u.Id)
|
||||
}
|
||||
|
||||
if len(u.TeamId) != 26 {
|
||||
return NewAppError("User.IsValid", "Invalid team id", "")
|
||||
}
|
||||
|
||||
if len(u.Username) == 0 || len(u.Username) > 64 {
|
||||
return NewAppError("User.IsValid", "Invalid username", "user_id="+u.Id)
|
||||
}
|
||||
|
||||
validChars, _ := regexp.Compile("^[a-z0-9\\.\\-\\_]+$")
|
||||
|
||||
if !validChars.MatchString(u.Username) {
|
||||
return NewAppError("User.IsValid", "Invalid username", "user_id="+u.Id)
|
||||
}
|
||||
|
||||
if len(u.Email) > 128 || len(u.Email) == 0 {
|
||||
return NewAppError("User.IsValid", "Invalid email", "user_id="+u.Id)
|
||||
}
|
||||
|
||||
if len(u.FullName) > 64 {
|
||||
return NewAppError("User.IsValid", "Invalid full name", "user_id="+u.Id)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// PreSave will set the Id and Username if missing. It will also fill
|
||||
// in the CreateAt, UpdateAt times. It will also hash the password. It should
|
||||
// be run before saving the user to the db.
|
||||
func (u *User) PreSave() {
|
||||
if u.Id == "" {
|
||||
u.Id = NewId()
|
||||
}
|
||||
|
||||
if u.Username == "" {
|
||||
u.Username = NewId()
|
||||
}
|
||||
|
||||
u.Username = strings.ToLower(u.Username)
|
||||
u.Email = strings.ToLower(u.Email)
|
||||
|
||||
u.CreateAt = GetMillis()
|
||||
u.UpdateAt = u.CreateAt
|
||||
|
||||
u.LastPasswordUpdate = u.CreateAt
|
||||
|
||||
if u.Props == nil {
|
||||
u.Props = make(map[string]string)
|
||||
}
|
||||
|
||||
if u.NotifyProps == nil || len(u.NotifyProps) == 0 {
|
||||
u.SetDefaultNotifications()
|
||||
}
|
||||
|
||||
if len(u.Password) > 0 {
|
||||
u.Password = HashPassword(u.Password)
|
||||
}
|
||||
}
|
||||
|
||||
// PreUpdate should be run before updating the user in the db.
|
||||
func (u *User) PreUpdate() {
|
||||
u.Username = strings.ToLower(u.Username)
|
||||
u.Email = strings.ToLower(u.Email)
|
||||
u.UpdateAt = GetMillis()
|
||||
|
||||
if u.NotifyProps == nil || len(u.NotifyProps) == 0 {
|
||||
u.SetDefaultNotifications()
|
||||
} else if _, ok := u.NotifyProps["mention_keys"]; ok {
|
||||
// Remove any blank mention keys
|
||||
splitKeys := strings.Split(u.NotifyProps["mention_keys"], ",")
|
||||
goodKeys := []string{}
|
||||
for _, key := range splitKeys {
|
||||
if len(key) > 0 {
|
||||
goodKeys = append(goodKeys, strings.ToLower(key))
|
||||
}
|
||||
}
|
||||
u.NotifyProps["mention_keys"] = strings.Join(goodKeys, ",")
|
||||
}
|
||||
}
|
||||
|
||||
func (u *User) SetDefaultNotifications() {
|
||||
u.NotifyProps = make(map[string]string)
|
||||
u.NotifyProps["email"] = "true"
|
||||
u.NotifyProps["desktop"] = USER_NOTIFY_ALL
|
||||
u.NotifyProps["desktop_sound"] = "true"
|
||||
u.NotifyProps["mention_keys"] = u.Username
|
||||
u.NotifyProps["first_name"] = "true"
|
||||
splitName := strings.Split(u.FullName, " ")
|
||||
if len(splitName) > 0 && splitName[0] != "" {
|
||||
u.NotifyProps["mention_keys"] += "," + splitName[0]
|
||||
}
|
||||
}
|
||||
|
||||
// ToJson convert a User to a json string
|
||||
func (u *User) ToJson() string {
|
||||
b, err := json.Marshal(u)
|
||||
if err != nil {
|
||||
return ""
|
||||
} else {
|
||||
return string(b)
|
||||
}
|
||||
}
|
||||
|
||||
// Generate a valid strong etag so the browser can cache the results
|
||||
func (u *User) Etag() string {
|
||||
return Etag(u.Id, u.UpdateAt)
|
||||
}
|
||||
|
||||
func (u *User) IsOffline() bool {
|
||||
return (GetMillis()-u.LastPingAt) > USER_OFFLINE_TIMEOUT && (GetMillis()-u.LastActivityAt) > USER_OFFLINE_TIMEOUT
|
||||
}
|
||||
|
||||
func (u *User) IsAway() bool {
|
||||
return (GetMillis() - u.LastActivityAt) > USER_AWAY_TIMEOUT
|
||||
}
|
||||
|
||||
// Remove any private data from the user object
|
||||
func (u *User) Sanitize(options map[string]bool) {
|
||||
u.Password = ""
|
||||
u.AuthData = ""
|
||||
|
||||
if len(options) != 0 && !options["email"] {
|
||||
u.Email = ""
|
||||
}
|
||||
if len(options) != 0 && !options["fullname"] {
|
||||
u.FullName = ""
|
||||
}
|
||||
if len(options) != 0 && !options["skypeid"] {
|
||||
// TODO - fill in when SkypeId is added to user model
|
||||
}
|
||||
if len(options) != 0 && !options["phonenumber"] {
|
||||
// TODO - fill in when PhoneNumber is added to user model
|
||||
}
|
||||
if len(options) != 0 && !options["passwordupadte"] {
|
||||
u.LastPasswordUpdate = 0
|
||||
}
|
||||
}
|
||||
|
||||
func (u *User) MakeNonNil() {
|
||||
if u.Props == nil {
|
||||
u.Props = make(map[string]string)
|
||||
}
|
||||
|
||||
if u.NotifyProps == nil {
|
||||
u.NotifyProps = make(map[string]string)
|
||||
}
|
||||
}
|
||||
|
||||
func (u *User) AddProp(key string, value string) {
|
||||
u.MakeNonNil()
|
||||
|
||||
u.Props[key] = value
|
||||
}
|
||||
|
||||
func (u *User) AddNotifyProp(key string, value string) {
|
||||
u.MakeNonNil()
|
||||
|
||||
u.NotifyProps[key] = value
|
||||
}
|
||||
|
||||
// UserFromJson will decode the input and return a User
|
||||
func UserFromJson(data io.Reader) *User {
|
||||
decoder := json.NewDecoder(data)
|
||||
var user User
|
||||
err := decoder.Decode(&user)
|
||||
if err == nil {
|
||||
return &user
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func UserMapToJson(u map[string]*User) string {
|
||||
b, err := json.Marshal(u)
|
||||
if err != nil {
|
||||
return ""
|
||||
} else {
|
||||
return string(b)
|
||||
}
|
||||
}
|
||||
|
||||
func UserMapFromJson(data io.Reader) map[string]*User {
|
||||
decoder := json.NewDecoder(data)
|
||||
var users map[string]*User
|
||||
err := decoder.Decode(&users)
|
||||
if err == nil {
|
||||
return users
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// HashPassword generates a hash using the bcrypt.GenerateFromPassword
|
||||
func HashPassword(password string) string {
|
||||
hash, err := bcrypt.GenerateFromPassword([]byte(password), 10)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return string(hash)
|
||||
}
|
||||
|
||||
// ComparePassword compares the hash
|
||||
func ComparePassword(hash string, password string) bool {
|
||||
|
||||
if len(password) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func IsUsernameValid(username string) bool {
|
||||
|
||||
var restrictedUsernames = []string {
|
||||
BOT_USERNAME,
|
||||
"all",
|
||||
"channel",
|
||||
}
|
||||
|
||||
for _,restrictedUsername := range restrictedUsernames {
|
||||
if username == restrictedUsername {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
92
model/user_test.go
Обычный файл
92
model/user_test.go
Обычный файл
@@ -0,0 +1,92 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPasswordHash(t *testing.T) {
|
||||
hash := HashPassword("Test")
|
||||
|
||||
if !ComparePassword(hash, "Test") {
|
||||
t.Fatal("Passwords don't match")
|
||||
}
|
||||
|
||||
if ComparePassword(hash, "Test2") {
|
||||
t.Fatal("Passwords should not have matched")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUserJson(t *testing.T) {
|
||||
user := User{Id: NewId(), Username: NewId()}
|
||||
json := user.ToJson()
|
||||
ruser := UserFromJson(strings.NewReader(json))
|
||||
|
||||
if user.Id != ruser.Id {
|
||||
t.Fatal("Ids do not match")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUserPreSave(t *testing.T) {
|
||||
user := User{Password: "test"}
|
||||
user.PreSave()
|
||||
user.Etag()
|
||||
}
|
||||
|
||||
func TestUserPreUpdate(t *testing.T) {
|
||||
user := User{Password: "test"}
|
||||
user.PreUpdate()
|
||||
}
|
||||
|
||||
func TestUserIsValid(t *testing.T) {
|
||||
user := User{}
|
||||
|
||||
if err := user.IsValid(); err == nil {
|
||||
t.Fatal()
|
||||
}
|
||||
|
||||
user.Id = NewId()
|
||||
if err := user.IsValid(); err == nil {
|
||||
t.Fatal()
|
||||
}
|
||||
|
||||
user.CreateAt = GetMillis()
|
||||
if err := user.IsValid(); err == nil {
|
||||
t.Fatal()
|
||||
}
|
||||
|
||||
user.UpdateAt = GetMillis()
|
||||
if err := user.IsValid(); err == nil {
|
||||
t.Fatal()
|
||||
}
|
||||
|
||||
user.TeamId = NewId()
|
||||
if err := user.IsValid(); err == nil {
|
||||
t.Fatal()
|
||||
}
|
||||
|
||||
user.Username = NewId() + "^hello#"
|
||||
if err := user.IsValid(); err == nil {
|
||||
t.Fatal()
|
||||
}
|
||||
|
||||
user.Username = NewId()
|
||||
user.Email = strings.Repeat("01234567890", 20)
|
||||
if err := user.IsValid(); err == nil {
|
||||
t.Fatal()
|
||||
}
|
||||
|
||||
user.Email = "test@nowhere.com"
|
||||
user.FullName = strings.Repeat("01234567890", 20)
|
||||
if err := user.IsValid(); err == nil {
|
||||
t.Fatal()
|
||||
}
|
||||
|
||||
user.FullName = ""
|
||||
if err := user.IsValid(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
320
model/utils.go
Обычный файл
320
model/utils.go
Обычный файл
@@ -0,0 +1,320 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"code.google.com/p/go-uuid/uuid"
|
||||
"encoding/base32"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/mail"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
ETAG_ROOT_VERSION = "10"
|
||||
)
|
||||
|
||||
type StringMap map[string]string
|
||||
type StringArray []string
|
||||
type EncryptStringMap map[string]string
|
||||
|
||||
// AppError is returned for any http response that's not in the 200 range.
|
||||
type AppError struct {
|
||||
Message string `json:"message"` // Message to be display to the end user without debugging information
|
||||
DetailedError string `json:"detailed_error"` // Internal error string to help the developer
|
||||
RequestId string `json:"request_id"` // The RequestId that's also set in the header
|
||||
StatusCode int `json:"status_code"` // The http status code
|
||||
Where string `json:"-"` // The function where it happened in the form of Struct.Func
|
||||
}
|
||||
|
||||
func (er *AppError) Error() string {
|
||||
return er.Where + ": " + er.Message + ", " + er.DetailedError
|
||||
}
|
||||
|
||||
func (er *AppError) ToJson() string {
|
||||
b, err := json.Marshal(er)
|
||||
if err != nil {
|
||||
return ""
|
||||
} else {
|
||||
return string(b)
|
||||
}
|
||||
}
|
||||
|
||||
// AppErrorFromJson will decode the input and return an AppError
|
||||
func AppErrorFromJson(data io.Reader) *AppError {
|
||||
decoder := json.NewDecoder(data)
|
||||
var er AppError
|
||||
err := decoder.Decode(&er)
|
||||
if err == nil {
|
||||
return &er
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func NewAppError(where string, message string, details string) *AppError {
|
||||
ap := &AppError{}
|
||||
ap.Message = message
|
||||
ap.Where = where
|
||||
ap.DetailedError = details
|
||||
ap.StatusCode = 500
|
||||
return ap
|
||||
}
|
||||
|
||||
var encoding = base32.NewEncoding("ybndrfg8ejkmcpqxot1uwisza345h769")
|
||||
|
||||
// NewId is a globally unique identifier. It is a [A-Z0-9] string 26
|
||||
// characters long. It is a UUID version 4 Guid that is zbased32 encoded
|
||||
// with the padding stripped off.
|
||||
func NewId() string {
|
||||
var b bytes.Buffer
|
||||
encoder := base32.NewEncoder(encoding, &b)
|
||||
encoder.Write(uuid.NewRandom())
|
||||
encoder.Close()
|
||||
b.Truncate(26) // removes the '==' padding
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// GetMillis is a convience method to get milliseconds since epoch.
|
||||
func GetMillis() int64 {
|
||||
return time.Now().UnixNano() / int64(time.Millisecond)
|
||||
}
|
||||
|
||||
// MapToJson converts a map to a json string
|
||||
func MapToJson(objmap map[string]string) string {
|
||||
if b, err := json.Marshal(objmap); err != nil {
|
||||
return ""
|
||||
} else {
|
||||
return string(b)
|
||||
}
|
||||
}
|
||||
|
||||
// MapFromJson will decode the key/value pair map
|
||||
func MapFromJson(data io.Reader) map[string]string {
|
||||
decoder := json.NewDecoder(data)
|
||||
|
||||
var objmap map[string]string
|
||||
if err := decoder.Decode(&objmap); err != nil {
|
||||
return make(map[string]string)
|
||||
} else {
|
||||
return objmap
|
||||
}
|
||||
}
|
||||
|
||||
func ArrayToJson(objmap []string) string {
|
||||
if b, err := json.Marshal(objmap); err != nil {
|
||||
return ""
|
||||
} else {
|
||||
return string(b)
|
||||
}
|
||||
}
|
||||
|
||||
func ArrayFromJson(data io.Reader) []string {
|
||||
decoder := json.NewDecoder(data)
|
||||
|
||||
var objmap []string
|
||||
if err := decoder.Decode(&objmap); err != nil {
|
||||
return make([]string, 0)
|
||||
} else {
|
||||
return objmap
|
||||
}
|
||||
}
|
||||
|
||||
func IsLower(s string) bool {
|
||||
if strings.ToLower(s) == s {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func IsValidEmail(email string) bool {
|
||||
|
||||
if !IsLower(email) {
|
||||
return false
|
||||
}
|
||||
|
||||
if _, err := mail.ParseAddress(email); err == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
var reservedDomains = []string{
|
||||
"www",
|
||||
"web",
|
||||
"admin",
|
||||
"support",
|
||||
"notify",
|
||||
"test",
|
||||
"demo",
|
||||
"mail",
|
||||
"team",
|
||||
"channel",
|
||||
"internal",
|
||||
"localhost",
|
||||
"stag",
|
||||
"post",
|
||||
"cluster",
|
||||
"api",
|
||||
}
|
||||
|
||||
func IsReservedDomain(s string) bool {
|
||||
s = strings.ToLower(s)
|
||||
|
||||
for _, value := range reservedDomains {
|
||||
if strings.Index(s, value) == 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func IsValidDomain(s string) bool {
|
||||
|
||||
if !IsValidAlphaNum(s) {
|
||||
return false
|
||||
}
|
||||
|
||||
if len(s) <= 3 {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
var wwwStart = regexp.MustCompile(`^www`)
|
||||
var betaStart = regexp.MustCompile(`^beta`)
|
||||
var ciStart = regexp.MustCompile(`^ci`)
|
||||
|
||||
func GetSubDomain(s string) (string, string) {
|
||||
s = strings.Replace(s, "http://", "", 1)
|
||||
s = strings.Replace(s, "https://", "", 1)
|
||||
|
||||
match := wwwStart.MatchString(s)
|
||||
if match {
|
||||
return "", ""
|
||||
}
|
||||
|
||||
match = betaStart.MatchString(s)
|
||||
if match {
|
||||
return "", ""
|
||||
}
|
||||
|
||||
match = ciStart.MatchString(s)
|
||||
if match {
|
||||
return "", ""
|
||||
}
|
||||
|
||||
parts := strings.Split(s, ".")
|
||||
|
||||
if len(parts) != 3 {
|
||||
return "", ""
|
||||
}
|
||||
|
||||
return parts[0], parts[1]
|
||||
}
|
||||
|
||||
func IsValidChannelIdentifier(s string) bool {
|
||||
|
||||
if !IsValidAlphaNum(s) {
|
||||
return false
|
||||
}
|
||||
|
||||
if len(s) < 2 {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
var validAlphaNum = regexp.MustCompile(`^[a-z0-9]+([a-z\-0-9]+|(__)?)[a-z0-9]+$`)
|
||||
|
||||
func IsValidAlphaNum(s string) bool {
|
||||
match := validAlphaNum.MatchString(s)
|
||||
|
||||
if !match {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func Etag(parts ...interface{}) string {
|
||||
|
||||
etag := ETAG_ROOT_VERSION
|
||||
|
||||
for _, part := range parts {
|
||||
etag += fmt.Sprintf(".%v", part)
|
||||
}
|
||||
|
||||
return etag
|
||||
}
|
||||
|
||||
var validHashtag = regexp.MustCompile(`^(#[A-Za-z]+[A-Za-z0-9_]*[A-Za-z0-9])$`)
|
||||
var puncStart = regexp.MustCompile(`^[.,()&$!\[\]{}"':;\\]+`)
|
||||
var puncEnd = regexp.MustCompile(`[.,()&$#!\[\]{}"':;\\]+$`)
|
||||
|
||||
func ParseHashtags(text string) (string, string) {
|
||||
words := strings.Split(strings.Replace(text, "\n", " ", -1), " ")
|
||||
|
||||
hashtagString := ""
|
||||
plainString := ""
|
||||
for _, word := range words {
|
||||
word = puncStart.ReplaceAllString(word, "")
|
||||
word = puncEnd.ReplaceAllString(word, "")
|
||||
if validHashtag.MatchString(word) {
|
||||
hashtagString += " " + word
|
||||
} else {
|
||||
plainString += " " + word
|
||||
}
|
||||
}
|
||||
|
||||
if len(hashtagString) > 1000 {
|
||||
hashtagString = hashtagString[:999]
|
||||
lastSpace := strings.LastIndex(hashtagString, " ")
|
||||
if lastSpace > -1 {
|
||||
hashtagString = hashtagString[:lastSpace]
|
||||
} else {
|
||||
hashtagString = ""
|
||||
}
|
||||
}
|
||||
|
||||
return strings.TrimSpace(hashtagString), strings.TrimSpace(plainString)
|
||||
}
|
||||
|
||||
func IsFileExtImage(ext string) bool {
|
||||
ext = strings.ToLower(ext)
|
||||
for _, imgExt := range IMAGE_EXTENSIONS {
|
||||
if ext == imgExt {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func GetImageMimeType(ext string) string {
|
||||
ext = strings.ToLower(ext)
|
||||
if len(IMAGE_MIME_TYPES[ext]) == 0 {
|
||||
return "image"
|
||||
} else {
|
||||
return IMAGE_MIME_TYPES[ext]
|
||||
}
|
||||
}
|
||||
|
||||
func ClearMentionTags(post string) string {
|
||||
post = strings.Replace(post, "<mention>", "", -1)
|
||||
post = strings.Replace(post, "</mention>", "", -1)
|
||||
return post
|
||||
}
|
||||
|
||||
var UrlRegex = regexp.MustCompile(`^((?:[a-z]+:\/\/)?(?:(?:[a-z0-9\-]+\.)+(?:[a-z]{2}|aero|arpa|biz|com|coop|edu|gov|info|int|jobs|mil|museum|name|nato|net|org|pro|travel|local|internal))(:[0-9]{1,5})?(?:\/[a-z0-9_\-\.~]+)*(\/([a-z0-9_\-\.]*)(?:\?[a-z0-9+_~\-\.%=&]*)?)?(?:#[a-zA-Z0-9!$&'()*+.=-_~:@/?]*)?)(?:\s+|$)$`)
|
||||
var PartialUrlRegex = regexp.MustCompile(`/api/v1/files/(get|get_image)/([A-Za-z0-9]{26})/([A-Za-z0-9]{26})/(([A-Za-z0-9]+/)?.+\.[A-Za-z0-9]{3,})`)
|
||||
118
model/utils_test.go
Обычный файл
118
model/utils_test.go
Обычный файл
@@ -0,0 +1,118 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNewId(t *testing.T) {
|
||||
for i := 0; i < 1000; i++ {
|
||||
id := NewId()
|
||||
if len(id) > 26 {
|
||||
t.Fatal("ids shouldn't be longer than 26 chars")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppError(t *testing.T) {
|
||||
err := NewAppError("TestAppError", "message", "")
|
||||
json := err.ToJson()
|
||||
rerr := AppErrorFromJson(strings.NewReader(json))
|
||||
if err.Message != rerr.Message {
|
||||
t.Fatal()
|
||||
}
|
||||
|
||||
err.Error()
|
||||
}
|
||||
|
||||
func TestMapJson(t *testing.T) {
|
||||
|
||||
m := make(map[string]string)
|
||||
m["id"] = "test_id"
|
||||
json := MapToJson(m)
|
||||
|
||||
rm := MapFromJson(strings.NewReader(json))
|
||||
|
||||
if rm["id"] != "test_id" {
|
||||
t.Fatal("map should be valid")
|
||||
}
|
||||
|
||||
rm2 := MapFromJson(strings.NewReader(""))
|
||||
if len(rm2) > 0 {
|
||||
t.Fatal("make should be ivalid")
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidEmail(t *testing.T) {
|
||||
if !IsValidEmail("corey@hulen.com") {
|
||||
t.Error("email should be valid")
|
||||
}
|
||||
|
||||
if IsValidEmail("@corey@hulen.com") {
|
||||
t.Error("should be invalid")
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidLower(t *testing.T) {
|
||||
if !IsLower("corey@hulen.com") {
|
||||
t.Error("should be valid")
|
||||
}
|
||||
|
||||
if IsLower("Corey@hulen.com") {
|
||||
t.Error("should be invalid")
|
||||
}
|
||||
}
|
||||
|
||||
var domains = []struct {
|
||||
value string
|
||||
expected bool
|
||||
}{
|
||||
{"spin-punch", true},
|
||||
{"-spin-punch", false},
|
||||
{"spin-punch-", false},
|
||||
{"spin_punch", false},
|
||||
{"a", false},
|
||||
{"aa", false},
|
||||
{"aaa", false},
|
||||
{"aaa-999b", true},
|
||||
{"b00b", true},
|
||||
{"b))b", false},
|
||||
{"test", true},
|
||||
}
|
||||
|
||||
func TestValidDomain(t *testing.T) {
|
||||
for _, v := range domains {
|
||||
if IsValidDomain(v.value) != v.expected {
|
||||
t.Errorf("expect %v as %v", v.value, v.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var tReservedDomains = []struct {
|
||||
value string
|
||||
expected bool
|
||||
}{
|
||||
{"test-hello", true},
|
||||
{"test", true},
|
||||
{"admin", true},
|
||||
{"Admin-punch", true},
|
||||
{"spin-punch-admin", false},
|
||||
}
|
||||
|
||||
func TestReservedDomain(t *testing.T) {
|
||||
for _, v := range tReservedDomains {
|
||||
if IsReservedDomain(v.value) != v.expected {
|
||||
t.Errorf("expect %v as %v", v.value, v.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEtag(t *testing.T) {
|
||||
etag := Etag("hello", 24)
|
||||
if len(etag) <= 0 {
|
||||
t.Fatal()
|
||||
}
|
||||
}
|
||||
Ссылка в новой задаче
Block a user